# HG changeset patch # User cin # Date 2018-12-27 00:57:22 # Node ID 4eaaaa5c1cf3adc6e0db8952179734488c405498 # Parent 2fd5aee498884b33f89cf658075a5b28fcd57cec ported string format traits to typescript diff --git a/src/amd/js/text/format-compile.js b/src/amd/js/text/format-compile.js deleted file mode 100644 --- a/src/amd/js/text/format-compile.js +++ /dev/null @@ -1,101 +0,0 @@ -define( - [], - function() { - var map = { - "\\{" : "&curlopen;", - "\\}" : "&curlclose;", - "&" : "&", - "\\:" : ":" - }; - - var rev = { - curlopen : "{", - curlclose : "}", - amp : "&", - colon : ":" - }; - - var espaceString = function(s) { - if (!s) - return s; - return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n","\\n") + "'"; - }; - - var encode = function(s) { - if (!s) - return s; - return s.replace(/\\{|\\}|&|\\:|\n/g, function(m) { - return map[m] || m; - }); - }; - - var decode = function(s) { - if (!s) - return s; - return s.replace(/&(\w+);/g, function(m, $1) { - return rev[$1] || m; - }); - }; - - var subst = function(s) { - var i = s.indexOf(":"), name, pattern; - if (i >= 0) { - name = s.substr(0, i); - pattern = s.substr(i + 1); - } else { - name = s; - } - - if (pattern) - return [ - espaceString(decode(name)), - espaceString(decode(pattern)) ]; - else - return [ espaceString(decode(name)) ]; - }; - - var compile = function(str) { - if (!str) - return function() {}; - - var chunks = encode(str).split("{"), chunk; - - var code = [ "var result=[];" ]; - - for (var i = 0; i < chunks.length; i++) { - chunk = chunks[i]; - - if (i === 0) { - if (chunk) - code.push("result.push(" + espaceString(decode(chunk)) + - ");"); - } else { - var len = chunk.indexOf("}"); - if (len < 0) - throw new Error("Unbalanced substitution #" + i); - - code.push("result.push(subst(" + - subst(chunk.substr(0, len)).join(",") + "));"); - if (chunk.length > len + 1) - code.push("result.push(" + - espaceString(decode(chunk.substr(len + 1))) + ");"); - } - } - - code.push("return result.join('');"); - - /* jshint -W054 */ - return new Function("subst", code.join("\n")); - }; - - var cache = {}; - - return function(template) { - var compiled = cache[template]; - if (!compiled) { - compiled = compile(template); - cache[template] = compiled; - } - return compiled; - }; - }); \ No newline at end of file diff --git a/src/amd/js/text/format.js b/src/amd/js/text/format.js deleted file mode 100644 --- a/src/amd/js/text/format.js +++ /dev/null @@ -1,87 +0,0 @@ -define([ - "../safe", - "./format-compile", - "dojo/number", - "dojo/date/locale", - "dojo/_base/array" ], function(safe, compile, number, date, array) { - - // {short,medium,full,long}-{date,time} - var convert = function(value, pattern) { - if (!pattern) - return value.toString(); - - if (pattern.toLocaleLowerCase() == "json") { - var cache = []; - return JSON.stringify(value, function(k, v) { - if (!safe.isPrimitive(v)) { - var id = array.indexOf(cache, v); - if (id >= 0) - return "@ref-" + id; - else - return v; - } else { - return v; - } - },2); - } - - if (safe.isNumber(value)) { - var nopt = {}; - if (pattern.indexOf("!") === 0) { - nopt.round = -1; - pattern = pattern.substr(1); - } - nopt.pattern = pattern; - return number.format(value, nopt); - } else if (value instanceof Date) { - var m = pattern.match(/^(\w+)-(\w+)$/); - if (m) - return date.format(value, { - selector : m[2], - formatLength : m[1] - }); - else if (pattern == "iso") - return value.toISOString(); - else - return date.format(value, { - selector : "date", - datePattern : pattern - }); - } else { - return value.toString(pattern); - } - }; - - function formatter(format) { - var data; - - if (arguments.length <= 1) - return format; - - data = Array.prototype.slice.call(arguments, 1); - - var template = compile(format); - - return template(function(name, pattern) { - var value = data[name]; - return !safe.isNull(value) ? convert(value, pattern) : ""; - }); - } - - formatter.compile = function(format) { - var template = compile(format); - - return function() { - var data = arguments; - - return template(function(name, pattern) { - var value = data[name]; - return !safe.isNull(value) ? convert(value, pattern) : ""; - }); - }; - }; - - formatter.convert = convert; - - return formatter; -}); \ No newline at end of file diff --git a/src/amd/ts/format.ts b/src/amd/ts/format.ts deleted file mode 100644 --- a/src/amd/ts/format.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { format as dojoFormatNumber } from "dojo/number"; -import { format as dojoFormatDate } from "dojo/date/locale"; - -import { isNumber } from "./safe"; - -interface NumberFormatOptions { - round?: number; - pattern?: string; -} - -function formatNumber(value: any, pattern: string) { - if (isNumber(value)) { - const nopt = {} as NumberFormatOptions; - if (pattern.indexOf("!") === 0) { - nopt.round = -1; - pattern = pattern.substr(1); - } - nopt.pattern = pattern; - - return dojoFormatNumber(value, nopt); - } -} - -function formatDate(value: any, pattern: string) { - if (value instanceof Date) { - const m = pattern.match(/^(\w+)-(\w+)$/); - if (m) - return dojoFormatDate(value, { - selector: m[2], - formatLength: m[1] - }); - else if (pattern === "iso") - return value.toISOString(); - else - return dojoFormatDate(value, { - selector: "date", - datePattern: pattern - }); - } -} diff --git a/src/amd/ts/text/format-compile.ts b/src/amd/ts/text/format-compile.ts new file mode 100644 --- /dev/null +++ b/src/amd/ts/text/format-compile.ts @@ -0,0 +1,8 @@ +import * as module from "module"; +import { TraceSource } from "../log/TraceSource"; + +const logger = TraceSource.get(module.id); + +logger.warn("The module is deprecated, use StringFormat.compile() method directly"); + +export { compile } from "./StringFormat"; diff --git a/src/amd/ts/text/format.ts b/src/amd/ts/text/format.ts new file mode 100644 --- /dev/null +++ b/src/amd/ts/text/format.ts @@ -0,0 +1,47 @@ +import { format as dojoFormatNumber } from "dojo/number"; +import { format as dojoFormatDate } from "dojo/date/locale"; +import { Formatter } from "./StringFormat"; + +import { isNumber } from "../safe"; + +interface NumberFormatOptions { + round?: number; + pattern?: string; +} + +function convertNumber(value: any, pattern: string) { + if (isNumber(value)) { + const nopt = {} as NumberFormatOptions; + if (pattern.indexOf("!") === 0) { + nopt.round = -1; + pattern = pattern.substr(1); + } + nopt.pattern = pattern; + + return dojoFormatNumber(value, nopt); + } +} + +function convertDate(value: any, pattern: string) { + if (value instanceof Date) { + const m = pattern.match(/^(\w+)-(\w+)$/); + if (m) + return dojoFormatDate(value, { + selector: m[2], + formatLength: m[1] + }); + else if (pattern === "iso") + return value.toISOString(); + else + return dojoFormatDate(value, { + selector: "date", + datePattern: pattern + }); + } +} + +const _formatter = new Formatter([convertNumber, convertDate]); + +export = function format(msg: string, ...args: any[]) { + return _formatter.format.apply(msg, ...args); +}; diff --git a/src/main/ts/interfaces.ts b/src/main/ts/interfaces.ts --- a/src/main/ts/interfaces.ts +++ b/src/main/ts/interfaces.ts @@ -2,6 +2,10 @@ export type Constructor = new (. export type Factory = (...args: any[]) => T; +export interface MapOf { + [key: string]: T; +} + export interface IDestroyable { destroy(); } diff --git a/src/main/ts/log/TraceSource.ts b/src/main/ts/log/TraceSource.ts --- a/src/main/ts/log/TraceSource.ts +++ b/src/main/ts/log/TraceSource.ts @@ -1,6 +1,6 @@ import { Observable } from "../Observable"; import { Registry } from "./Registry"; -import { format } from "../text/FormatString"; +import { format } from "../text/StringFormat"; export const DebugLevel = 400; @@ -38,7 +38,7 @@ export class TraceSource { } protected emit(level: number, arg: any) { - this._notifyNext({ source: this, level, arg}); + this._notifyNext({ source: this, level, arg }); } isDebugEnabled() { diff --git a/src/main/ts/text/FormatString.ts b/src/main/ts/text/FormatString.ts deleted file mode 100644 --- a/src/main/ts/text/FormatString.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { isPrimitive, isNumber, isNull } from "../safe"; - -type SubstFn = (name: string, format?: string) => string; -type FormatFn = (subst: SubstFn) => string; -type ConvertFn = (value: any, format?: string) => string; - -const map = { - "\\{": "&curlopen;", - "\\}": "&curlclose;", - "&": "&", - "\\:": ":" -}; - -const rev = { - curlopen: "{", - curlclose: "}", - amp: "&", - colon: ":" -}; - -function espaceString(s: string) { - if (!s) - return s; - return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n", "\\n") + "'"; -} - -function encode(s: string) { - if (!s) - return s; - return s.replace(/\\{|\\}|&|\\:|\n/g, m => map[m] || m); -} - -function decode(s: string) { - if (!s) - return s; - return s.replace(/&(\w+);/g, (m, $1) => rev[$1] || m); -} - -function subst(s: string) { - const i = s.indexOf(":"); - let name: string; - let pattern: string; - if (i >= 0) { - name = s.substr(0, i); - pattern = s.substr(i + 1); - } else { - name = s; - } - - if (pattern) - return [ - espaceString(decode(name)), - espaceString(decode(pattern))]; - else - return [espaceString(decode(name))]; -} - -function _compile(str: string) { - if (!str) - return () => void 0; - - const chunks = encode(str).split("{"); - let chunk: string; - - const code = ["var result=[];"]; - - for (let i = 0; i < chunks.length; i++) { - chunk = chunks[i]; - - if (i === 0) { - if (chunk) - code.push("result.push(" + espaceString(decode(chunk)) + - ");"); - } else { - const len = chunk.indexOf("}"); - if (len < 0) - throw new Error("Unbalanced substitution #" + i); - - code.push("result.push(subst(" + - subst(chunk.substr(0, len)).join(",") + "));"); - if (chunk.length > len + 1) - code.push("result.push(" + - espaceString(decode(chunk.substr(len + 1))) + ");"); - } - } - - code.push("return result.join('');"); - - // the code for this function is generated from the template - // tslint:disable-next-line:function-constructor - return new Function("subst", code.join("\n")) as FormatFn; -} - -const cache = {} as { - [i: string]: FormatFn -}; - -export function compile(template: string) { - let compiled = cache[template]; - if (!compiled) { - compiled = _compile(template); - cache[template] = compiled; - } - return compiled; -} - -function convert(value: any, pattern) { - if (!pattern) - return value.toString(); - - if (pattern.toLocaleLowerCase() === "json") { - const seen = []; - return JSON.stringify(value, (k, v) => { - if (!isPrimitive(v)) { - const id = seen.indexOf(v); - if (id >= 0) - return "@ref-" + id; - else { - seen.push(v); - return v; - } - } else { - return v; - } - }, 2); - } - - defaultFormatter(value, pattern); -} - -function defaultFormatter(value: any, pattern: string) { - if (value instanceof Date) { - return value.toISOString(); - } else { - return value.toString(pattern); - } -} - -export function format(fmt: string, ...args: any[]) { - if (args.length === 0) - return fmt; - - const template = compile(fmt); - - return template((name, pattern) => { - const value = args[name]; - return !isNull(value) ? convert(value, pattern) : ""; - }); -} - -export function compileFormat(fmt: string) { - const template = compile(fmt); - - return (...args: any[]) => { - return template((name, pattern) => { - const value = args[name]; - return !isNull(value) ? convert(value, pattern) : ""; - }); - }; -} diff --git a/src/main/ts/text/StringFormat.ts b/src/main/ts/text/StringFormat.ts new file mode 100644 --- /dev/null +++ b/src/main/ts/text/StringFormat.ts @@ -0,0 +1,173 @@ +import { isPrimitive, isNull, each } from "../safe"; +import { MapOf } from "../interfaces"; + +type SubstFn = (name: string, format?: string) => string; +type TemplateFn = (subst: SubstFn) => string; +type ConvertFn = (value: any, format?: string) => string; + +const map = { + "\\{": "&curlopen;", + "\\}": "&curlclose;", + "&": "&", + "\\:": ":" +}; + +const rev = { + curlopen: "{", + curlclose: "}", + amp: "&", + colon: ":" +}; + +function espaceString(s: string) { + if (!s) + return s; + return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n", "\\n") + "'"; +} + +function encode(s: string) { + if (!s) + return s; + return s.replace(/\\{|\\}|&|\\:|\n/g, m => map[m] || m); +} + +function decode(s: string) { + if (!s) + return s; + return s.replace(/&(\w+);/g, (m, $1) => rev[$1] || m); +} + +function subst(s: string) { + const i = s.indexOf(":"); + let name: string; + let pattern: string; + if (i >= 0) { + name = s.substr(0, i); + pattern = s.substr(i + 1); + } else { + name = s; + } + + if (pattern) + return [ + espaceString(decode(name)), + espaceString(decode(pattern))]; + else + return [espaceString(decode(name))]; +} + +function _compile(str: string) { + if (!str) + return () => void 0; + + const chunks = encode(str).split("{"); + let chunk: string; + + const code = ["var result=[];"]; + + for (let i = 0; i < chunks.length; i++) { + chunk = chunks[i]; + + if (i === 0) { + if (chunk) + code.push("result.push(" + espaceString(decode(chunk)) + + ");"); + } else { + const len = chunk.indexOf("}"); + if (len < 0) + throw new Error("Unbalanced substitution #" + i); + + code.push("result.push(subst(" + + subst(chunk.substr(0, len)).join(",") + "));"); + if (chunk.length > len + 1) + code.push("result.push(" + + espaceString(decode(chunk.substr(len + 1))) + ");"); + } + } + + code.push("return result.join('');"); + + // the code for this function is generated from the template + // tslint:disable-next-line:function-constructor + return new Function("subst", code.join("\n")) as TemplateFn; +} + +const cache: MapOf = {}; + +export function compile(template: string) { + let compiled = cache[template]; + if (!compiled) { + compiled = _compile(template); + cache[template] = compiled; + } + return compiled; +} + +function defaultConverter(value: any, pattern: string) { + if (pattern && pattern.toLocaleLowerCase() === "json") { + const seen = []; + return JSON.stringify(value, (k, v) => { + if (!isPrimitive(v)) { + const id = seen.indexOf(v); + if (id >= 0) + return "@ref-" + id; + else { + seen.push(v); + return v; + } + } else { + return v; + } + }, 2); + } else if (isNull(value)) { + return ""; + } else if (value instanceof Date) { + return value.toISOString(); + } else { + return pattern ? value.toString(pattern) : value.toString(); + } +} + +export class Formatter { + _converters: ConvertFn[]; + + constructor(converters?: ConvertFn[]) { + this._converters = converters || []; + this._converters.push(defaultConverter); + } + + convert(value: any, pattern: string) { + for (const c of this._converters) { + const res = c(value, pattern); + if (!isNull(res)) + return res; + } + return ""; + } + + format(msg: string, ...args: any[]) { + const template = compile(msg); + + return template((name, pattern) => { + const value = args[name]; + return !isNull(value) ? this.convert(value, pattern) : ""; + }); + + } + + compile(msg: string) { + const template = compile(msg); + return (...args: any[]) => { + return template((name, pattern) => { + const value = args[name]; + return !isNull(value) ? this.convert(value, pattern) : ""; + }); + }; + } +} + +const _default = new Formatter(); + +export function format(msg: string, ...args: any[]) { + return _default.format(msg, ...args); +}