From 766d886c079074ec72f0521ef2e95a1b083145dd Mon Sep 17 00:00:00 2001 From: Lukasz Mitusinski Date: Wed, 4 Apr 2018 14:55:36 +0200 Subject: [PATCH] java parser and template --- src/parsers/java.ts | 41 ++++++++++ src/writers/index.ts | 5 ++ src/writers/java.ts | 188 +++++++++++++++++++++++++++++++++++++++++++ templates/java.njk | 48 +++++++++++ 4 files changed, 282 insertions(+) create mode 100644 src/parsers/java.ts create mode 100644 src/writers/java.ts create mode 100644 templates/java.njk diff --git a/src/parsers/java.ts b/src/parsers/java.ts new file mode 100644 index 0000000..f53248c --- /dev/null +++ b/src/parsers/java.ts @@ -0,0 +1,41 @@ + +import * as path from 'path'; + +import { + exec as execSync +} from 'child_process'; + +import { + promisify +} from 'util'; + +import { + JsonParser +} from './json'; + +import { + IDefinition +} from './formatTypes'; + +const exec = promisify(execSync); + +// Path to python implementation of the parser +const PYTHON_HELPER = path.resolve(__dirname, 'python_parser.py'); + + +/** + * Parser for generating widgets from Widget definitions in python. + */ +export +class JavaParser extends JsonParser { + + start(): Promise { + // This calls out to an implementation in python, that pipes back JSON + // in our custom format, see JsonParser. + const cmd = `python "${PYTHON_HELPER}" "${this.input}"`; + return exec(cmd, {windowsHide: true} as any).then(({stdout, stderr}) => { + const data = JSON.parse(stdout as any as string) as IDefinition; + return this.processDefinition(data); + }); + } +} diff --git a/src/writers/index.ts b/src/writers/index.ts index 6194404..0a5f382 100644 --- a/src/writers/index.ts +++ b/src/writers/index.ts @@ -8,6 +8,10 @@ import { PythonWriter } from './python'; +import { + JavaWriter +} from './java'; + import { JSES5Writer, JSES6Writer, TSWriter } from './js'; @@ -27,4 +31,5 @@ let writers: {[key: string]: IWriterConstructor| undefined} = { 'js-es6': JSES6Writer, es6: JSES6Writer, ts: TSWriter, + java: JavaWriter, } \ No newline at end of file diff --git a/src/writers/java.ts b/src/writers/java.ts new file mode 100644 index 0000000..fc77e1e --- /dev/null +++ b/src/writers/java.ts @@ -0,0 +1,188 @@ +import * as path from 'path'; + +import { + TemplateWriter, TemplateState +} from './template'; + +import { + Attributes, +} from '../core'; + + +const INDENT = ' '; + + +export +class JavaWriter extends TemplateWriter { + /** + * + */ + constructor(output: string, options: Partial = {}) { + super(output, { + fileExt: 'java', + template: path.resolve(__dirname, '../../templates/java.njk'), + ...options, + }); + this.env.addFilter('uppercase', function(str) { + return str.toUpperCase( ) + }); + this.env.addFilter('camelcase', function(str) { + var parsed_str = "" + var splitted = str.split("_") + for (let word of splitted) { + parsed_str += word.charAt(0).toUpperCase() + word.slice(1) + } + return parsed_str; + }); + this.env.addFilter('fromlower', function(str) { + return str.charAt(0).toLowerCase() + str.slice(1) + }); + this.env.addFilter('javatypes', function(str) { + switch(str) { + case 'string': { + return 'String' + } + case 'boolean': { + return 'boolean' + } + case 'int': { + return 'int' + } + case 'float': { + return 'float' + } + case 'array': { + return 'ArrayList' + } + default: { + return 'Object' + } + } + }); + } + + transformState(data: TemplateState): TemplateState { + data = super.transformState(data); + data.widgets = data.widgets.map((widget) => { + let {properties} = widget; + return { + ...widget, + properties: properties ? Object.keys(properties).reduce((res, key) => { + let attr = properties![key]; + res[key] = { + ...attr, + traitDef: makeTrait(attr), + } + return res; + }, {} as TemplateState) : properties, + }; + }); + return data; + } + + finalize(): Promise { + return super.finalize() + } +} + + + +function convertValue(value: any): string { + if (value === true) { + return 'true'; + } else if (value === false) { + return 'false'; + } else if (value === null) { + return 'null'; + } else if (value === undefined) { + return 'null'; + } else if (Array.isArray(value)) { + return `${value.map(v => convertValue(v)).join(', ')}`; + } else if (typeof value === 'string') { + return `"${value.toString()}"`; + } + return value.toString(); +} + + +function makeTrait(data: Attributes.Attribute, innerTrait=false): string { + let traitDef: string = 'Any()'; + + if (data === null) { + + traitDef = 'null;'; + + } else if (data === undefined) { + + traitDef = 'null;'; + + } else if (typeof data === 'string') { + + traitDef = `"${convertValue(data)};"`; + + } else if (typeof data === 'number') { + + if (Number.isInteger(data)) { + traitDef = `${data};`; + } else { + traitDef = `${data};`; + } + + } else if (typeof data === 'boolean') { + + traitDef = `${convertValue(data)};` + + } else { + + let allowNoneArg = ''; + if (data.allowNull !== undefined && data.allowNull !== false) { + allowNoneArg = `, allow_none=${convertValue(data.allowNull)}`; + } + if (Attributes.isUnion(data)) { + + const defs = data.oneOf.map((subdata) => makeTrait(subdata, true)); + traitDef = `Union([\n${INDENT}${INDENT}${defs.join(`,\n${INDENT}${INDENT}`)}\n${INDENT}]${allowNoneArg})`; + + } else { + + let defValue = convertValue(data.default); + switch (data.type) { + + case 'object': + traitDef = `null`; + break; + + case 'array': + let items = data.items; + if (items === undefined) { + traitDef = `new ArrayList<>()`; + } else if (Array.isArray(items)) { + if (defValue) { + traitDef = (`Arrays.asList(${defValue})`); + } else { + traitDef = `new ArrayList<>()`; + } + } else { + traitDef = `Arrays.asList(${makeTrait(items, true)}))` + } + break; + + case 'widgetRef': + traitDef = `new ${data.widgetType}()`; + break; + + case 'ndarray': + traitDef = `null; //TODO` + break; + + case 'dataunion': + traitDef = `null; //TODO` + break; + + default: + traitDef = `${defValue}`; + } + } + } + return traitDef +} diff --git a/templates/java.njk b/templates/java.njk new file mode 100644 index 0000000..245c5a8 --- /dev/null +++ b/templates/java.njk @@ -0,0 +1,48 @@ +package //TODO package name +{% block widgets %} +{% for widget in widgets %} +{% block widget %} + +public class {{ widget.name }} extends {{ widget.inherits | join(", ") }} { + + + + {% for propName, prop in widget.properties %} + {% if propName.startsWith("_") %} + public static final String {{propName.slice(1)|upper}}_VALUE = "{{prop.default}}"; + {% else %} + public static final String {{propName|uppercase}} = "{{propName}}"; + {% endif %} + {% endfor %} + + {% for propName, prop in widget.properties %} + {% if not propName.startsWith("_") %} + private {% if prop.type %}{{prop.type|javatypes}}{% else %}Object{% endif %} {{propName|camelcase|fromlower}} = {{prop.traitDef}}; + {% endif %} + {% endfor %} + + public {{widget.name}}() { + super(); + openComm(); + } + +{% for propName, prop in widget.properties %} + {% if not propName.startsWith("_") %} + public {% if prop.type %}{{prop.type|javatypes}}{% else %}Object{% endif %} get{{propName|camelcase}}() { + return {{propName|camelcase|fromlower}}; + } + public void set{{propName|camelcase}}({% if prop.type %}{{prop.type|javatypes}}{% else %}Object{% endif %} {{propName}}){ + this.{{propName|camelcase|fromlower}} = {{propName|camelcase|fromlower}}; + sendUpdate({{propName|uppercase}}, {{propName|camelcase|fromlower}}); + } + {% else %} + public String get{{propName|camelcase}}Value(){ + return {{propName.slice(1)|upper}}_VALUE; + } + {% endif %} + +{% endfor %} +} +{% endblock %} +{% endfor %} +{% endblock %}