@@ -13,19 +13,29 @@ function toLittleHorseSkeleton(model) {
1313
1414 processes . forEach ( ( processNode ) => {
1515 const className = sanitizeClassName ( processNode . name ) ;
16- lines . push ( `public class ${ className } Workflow {` ) ;
16+ const workflowName = toKebabName ( processNode . name ) ;
17+ lines . push ( `public final class ${ className } Workflow {` ) ;
1718 lines . push ( " // TODO: adapt this skeleton to your LittleHorse SDK version." ) ;
18- lines . push ( " public void buildWorkflow() {" ) ;
19- lines . push ( ` // Process: ${ processNode . name } ` ) ;
20- lines . push ( " // TODO: declare variables and task definitions" ) ;
19+ lines . push ( " public static Workflow getWorkflow() {" ) ;
20+ lines . push ( ` return new WorkflowImpl("${ workflowName } ", wf -> {` ) ;
21+ lines . push ( ` // Process: ${ processNode . name } ` ) ;
22+ const declarations = renderDeclarations ( processNode . body || [ ] , 6 ) ;
23+ if ( declarations . length > 0 ) {
24+ lines . push ( " // TODO: declare variables and task definitions" ) ;
25+ lines . push ( ...declarations ) ;
26+ lines . push ( "" ) ;
27+ } else {
28+ lines . push ( " // TODO: declare variables and task definitions" ) ;
29+ }
2130
22- const bodyLines = renderStatements ( processNode . body || [ ] , 4 ) ;
31+ const bodyLines = renderStatements ( processNode . body || [ ] , 6 ) ;
2332 if ( bodyLines . length > 0 ) {
2433 lines . push ( ...bodyLines ) ;
2534 } else {
26- lines . push ( " // TODO: add workflow steps" ) ;
35+ lines . push ( " // TODO: add workflow steps" ) ;
2736 }
2837
38+ lines . push ( " });" ) ;
2939 lines . push ( " }" ) ;
3040 lines . push ( "}" ) ;
3141 lines . push ( "" ) ;
@@ -34,6 +44,134 @@ function toLittleHorseSkeleton(model) {
3444 return `${ lines . join ( "\n" ) . trimEnd ( ) } \n` ;
3545}
3646
47+ function renderDeclarations ( statements , indentSize ) {
48+ const indent = " " . repeat ( indentSize ) ;
49+ const entries = collectFieldUsage ( statements ) ;
50+ const lines = [ ] ;
51+
52+ for ( const entry of entries ) {
53+ const decl = renderDeclaration ( entry ) ;
54+ if ( decl ) {
55+ lines . push ( `${ indent } ${ decl } ` ) ;
56+ }
57+ }
58+
59+ return lines ;
60+ }
61+
62+ function collectFieldUsage ( statements , usage = new Map ( ) ) {
63+ for ( const statement of statements ) {
64+ if ( statement . type === "when" ) {
65+ continue ;
66+ }
67+
68+ if ( statement . type === "if" ) {
69+ collectConditionUsage ( statement . condition , usage ) ;
70+ collectFieldUsage ( statement . then || [ ] , usage ) ;
71+ for ( const branch of statement . elseIf || [ ] ) {
72+ collectConditionUsage ( branch . condition , usage ) ;
73+ collectFieldUsage ( branch . then || [ ] , usage ) ;
74+ }
75+ if ( statement . else && Array . isArray ( statement . else . body ) ) {
76+ collectFieldUsage ( statement . else . body , usage ) ;
77+ }
78+ continue ;
79+ }
80+
81+ if ( statement . type === "assign" || statement . type === "transition" || statement . type === "update" ) {
82+ recordField ( usage , statement . target , statement . value ) ;
83+ continue ;
84+ }
85+
86+ if ( statement . type === "notify" ) {
87+ continue ;
88+ }
89+
90+ if ( statement . type === "create" ) {
91+ continue ;
92+ }
93+
94+ if ( statement . type === "require" ) {
95+ continue ;
96+ }
97+ }
98+
99+ return Array . from ( usage . values ( ) ) ;
100+ }
101+
102+ function collectConditionUsage ( condition , usage ) {
103+ if ( ! condition ) {
104+ return ;
105+ }
106+
107+ if ( condition . type === "logical" ) {
108+ for ( const child of condition . conditions || [ ] ) {
109+ collectConditionUsage ( child , usage ) ;
110+ }
111+ return ;
112+ }
113+
114+ if ( condition . left && condition . left . type === "field" ) {
115+ recordField ( usage , condition . left . path , condition . right ) ;
116+ }
117+ }
118+
119+ function recordField ( usage , path , value ) {
120+ if ( ! path ) {
121+ return ;
122+ }
123+ const key = String ( path ) ;
124+ const existing = usage . get ( key ) ;
125+ const inferred = inferType ( value ) ;
126+ if ( ! existing ) {
127+ usage . set ( key , {
128+ path : key ,
129+ type : inferred ,
130+ } ) ;
131+ return ;
132+ }
133+ existing . type = mergeTypes ( existing . type , inferred ) ;
134+ }
135+
136+ function inferType ( value ) {
137+ if ( ! value ) {
138+ return "string" ;
139+ }
140+ if ( value . type === "number" ) {
141+ return "number" ;
142+ }
143+ if ( value . type === "boolean" ) {
144+ return "boolean" ;
145+ }
146+ if ( value . type === "string" ) {
147+ return "string" ;
148+ }
149+ return "string" ;
150+ }
151+
152+ function mergeTypes ( existing , incoming ) {
153+ if ( existing === incoming ) {
154+ return existing ;
155+ }
156+ if ( existing === "number" || incoming === "number" ) {
157+ return "number" ;
158+ }
159+ if ( existing === "boolean" || incoming === "boolean" ) {
160+ return "boolean" ;
161+ }
162+ return "string" ;
163+ }
164+
165+ function renderDeclaration ( entry ) {
166+ const variableName = toVariableName ( entry . path ) ;
167+ if ( ! variableName ) {
168+ return null ;
169+ }
170+
171+ const typeSuffix = entry . type === "number" ? "Double" : entry . type === "boolean" ? "Boolean" : "Str" ;
172+ return `var ${ variableName } = wf.declare${ typeSuffix } ("${ variableName } ");` ;
173+ }
174+
37175function renderStatements ( statements , indentSize ) {
38176 const lines = [ ] ;
39177 const indent = " " . repeat ( indentSize ) ;
@@ -45,16 +183,17 @@ function renderStatements(statements, indentSize) {
45183 }
46184
47185 if ( statement . type === "if" ) {
48- lines . push ( `${ indent } // if ${ formatCondition ( statement . condition ) } : ` ) ;
186+ lines . push ( `${ indent } wf.doIf(/* ${ formatCondition ( statement . condition ) } */, ifBody -> { ` ) ;
49187 lines . push ( ...renderStatements ( statement . then || [ ] , indentSize + 2 ) ) ;
188+ lines . push ( `${ indent } });` ) ;
50189
51190 for ( const branch of statement . elseIf || [ ] ) {
52- lines . push ( `${ indent } // else if ${ formatCondition ( branch . condition ) } : ` ) ;
191+ lines . push ( `${ indent } // else if ${ formatCondition ( branch . condition ) } (map to doElseIf) ` ) ;
53192 lines . push ( ...renderStatements ( branch . then || [ ] , indentSize + 2 ) ) ;
54193 }
55194
56195 if ( statement . else && ( statement . else . body || [ ] ) . length > 0 ) {
57- lines . push ( `${ indent } // else: ` ) ;
196+ lines . push ( `${ indent } // else (map to doElse) ` ) ;
58197 lines . push ( ...renderStatements ( statement . else . body || [ ] , indentSize + 2 ) ) ;
59198 }
60199 continue ;
@@ -65,38 +204,48 @@ function renderStatements(statements, indentSize) {
65204 continue ;
66205 }
67206
68- lines . push ( `${ indent } // ${ formatAction ( statement ) } ` ) ;
207+ const action = formatAction ( statement ) ;
208+ if ( action ) {
209+ lines . push ( `${ indent } ${ action } ` ) ;
210+ continue ;
211+ }
212+
213+ lines . push ( `${ indent } // ${ statement . type } ` ) ;
69214 }
70215
71216 return lines ;
72217}
73218
74219function formatAction ( statement ) {
75220 if ( statement . type === "assign" ) {
76- return `assign ${ statement . target || "?" } = ${ formatExpression ( statement . value ) } ` ;
221+ return `// assign ${ statement . target || "?" } = ${ formatExpression ( statement . value ) } ` ;
77222 }
78223
79224 if ( statement . type === "transition" ) {
80- return `transition ${ statement . target || "?" } -> ${ formatExpression ( statement . value ) } ` ;
225+ return `// transition ${ statement . target || "?" } -> ${ formatExpression ( statement . value ) } ` ;
81226 }
82227
83228 if ( statement . type === "notify" ) {
84- return `notify ${ statement . target } "${ statement . message } "` ;
229+ const target = statement . target || "target" ;
230+ const message = statement . message ? `"${ statement . message } "` : "\"message\"" ;
231+ return `wf.execute("notify", "${ target } ", ${ message } );` ;
85232 }
86233
87234 if ( statement . type === "create" ) {
88- return `create ${ statement . entity } ` ;
235+ const entity = statement . entity || "entity" ;
236+ return `wf.execute("create", "${ entity } ");` ;
89237 }
90238
91239 if ( statement . type === "update" ) {
92- return `update ${ statement . target || "?" } = ${ formatExpression ( statement . value ) } ` ;
240+ return `wf.execute(" update", " ${ statement . target || "target" } ", ${ formatExpression ( statement . value ) } ); ` ;
93241 }
94242
95243 if ( statement . type === "require" ) {
96- return `require ${ statement . requirement } ` ;
244+ const requirement = statement . requirement || "requirement" ;
245+ return `wf.execute("require", "${ requirement } ");` ;
97246 }
98247
99- return statement . type ;
248+ return null ;
100249}
101250
102251function formatCondition ( condition ) {
@@ -146,6 +295,33 @@ function sanitizeClassName(value) {
146295 return safe . charAt ( 0 ) . toUpperCase ( ) + safe . slice ( 1 ) ;
147296}
148297
298+ function toKebabName ( value ) {
299+ const raw = String ( value || "workflow" ) . trim ( ) ;
300+ if ( ! raw ) {
301+ return "workflow" ;
302+ }
303+ const spaced = raw . replace ( / ( [ a - z 0 - 9 ] ) ( [ A - Z ] ) / g, "$1 $2" ) ;
304+ return spaced
305+ . replace ( / [ ^ A - Z a - z 0 - 9 ] + / g, "-" )
306+ . replace ( / ^ - + | - + $ / g, "" )
307+ . toLowerCase ( ) ;
308+ }
309+
310+ function toVariableName ( path ) {
311+ const value = String ( path || "" ) . trim ( ) ;
312+ if ( ! value ) {
313+ return null ;
314+ }
315+ const sanitized = value . replace ( / [ ^ A - Z a - z 0 - 9 ] + / g, "_" ) . replace ( / ^ _ + | _ + $ / g, "" ) ;
316+ if ( ! sanitized ) {
317+ return null ;
318+ }
319+ if ( / ^ [ A - Z a - z _ ] / . test ( sanitized ) ) {
320+ return sanitized ;
321+ }
322+ return `var_${ sanitized } ` ;
323+ }
324+
149325module . exports = {
150326 toLittleHorseSkeleton,
151327} ;
0 commit comments