44import java .util .ArrayList ;
55import java .util .List ;
66
7+ import liquidjava .rj_language .ast .*;
8+ import liquidjava .rj_language .parsing .RefinementsParser ;
79import liquidjava .utils .Utils ;
810import spoon .Launcher ;
911import spoon .reflect .CtModel ;
@@ -86,7 +88,7 @@ private static CtType<?> getType(CtModel model) {
8688 }
8789
8890 /**
89- * Extracts the simple name from a class or interface
91+ * Gets the simple name from a class or interface
9092 * Uses name from ExternalRefinementsFor if present, otherwise uses class or interface name
9193 * @param ctType
9294 * @return class name
@@ -102,7 +104,7 @@ private static String getClassName(CtType<?> ctType) {
102104 }
103105
104106 /**
105- * Extracts the possible states from a class or interface
107+ * Gets the possible states from a class or interface
106108 * @param ctType
107109 * @return list of states
108110 */
@@ -117,7 +119,7 @@ private static List<String> getStates(CtType<?> ctType) {
117119 }
118120
119121 /**
120- * Extracts the initial state from a class
122+ * Gets the initial state from a class
121123 * If not explicitely defined, uses the first state in the state set
122124 * @param ctClass
123125 * @return initial state
@@ -126,16 +128,19 @@ private static String getInitialStateFromClass(CtClass<?> ctClass, List<String>
126128 for (CtConstructor <?> constructor : ctClass .getConstructors ()) {
127129 for (CtAnnotation <?> annotation : constructor .getAnnotations ()) {
128130 if (annotation .getAnnotationType ().getSimpleName ().equals (STATE_REFINEMENT_ANNOTATION )) {
129- Object to = annotation .getValueAsObject ("to" );
130- return normalizeState (to );
131+ String to = annotation .getValueAsString ("to" );
132+ List <String > parsedStates = parseStateExpression (to , states );
133+ if (!parsedStates .isEmpty ()) {
134+ return parsedStates .getFirst ();
135+ }
131136 }
132137 }
133138 }
134- return states .isEmpty () ? null : states . getFirst ();
139+ return states .getFirst ();
135140 }
136141
137142 /**
138- * Extracts the initial state from an interface
143+ * Gets the initial state from an interface
139144 * If not explicitely defined, uses the first state in the state set
140145 * @param ctInterface
141146 * @param className
@@ -146,8 +151,11 @@ private static String getInitialStateFromInterface(CtInterface<?> ctInterface, S
146151 if (method .getSimpleName ().equals (className )) {
147152 for (CtAnnotation <?> annotation : method .getAnnotations ()) {
148153 if (annotation .getAnnotationType ().getSimpleName ().equals (STATE_REFINEMENT_ANNOTATION )) {
149- Object to = annotation .getValueAsObject ("to" );
150- return normalizeState (to );
154+ String to = annotation .getValueAsString ("to" );
155+ List <String > parsedStates = parseStateExpression (to , states );
156+ if (!parsedStates .isEmpty ()) {
157+ return parsedStates .getFirst ();
158+ }
151159 }
152160 }
153161 }
@@ -156,7 +164,7 @@ private static String getInitialStateFromInterface(CtInterface<?> ctInterface, S
156164 }
157165
158166 /**
159- * Extracts transitions from a class
167+ * Gets transitions from a class
160168 * @param ctClass
161169 * @param states
162170 * @return list of StateMachineTransition
@@ -176,7 +184,7 @@ private static List<StateMachineTransition> getTransitionsFromClass(CtClass<?> c
176184 }
177185
178186 /**
179- * Extracts transitions from an interface
187+ * Gets transitions from an interface
180188 * @param ctInterface
181189 * @param className
182190 * @param states
@@ -198,60 +206,84 @@ private static List<StateMachineTransition> getTransitionsFromInterface(CtInterf
198206 }
199207
200208 /**
201- * Extracts transitions from the given annotation
202- * @param annotation
203- * @param methodName
209+ * Gets transitions from the given annotation
210+ * @param ann
211+ * @param method
204212 * @param states
205213 * @return list of StateMachineTransition
206214 */
207- private static List <StateMachineTransition > getTransitions (CtAnnotation <?> annotation , String methodName , List <String > states ) {
215+ private static List <StateMachineTransition > getTransitions (CtAnnotation <?> ann , String method , List <String > states ) {
208216 List <StateMachineTransition > transitions = new ArrayList <>();
209- String from = normalizeState ( annotation . getValueAsObject ("from" ) );
210- String to = normalizeState ( annotation . getValueAsObject ("to" ) );
217+ String from = ann . getValueAsString ("from" );
218+ String to = ann . getValueAsString ("to" );
211219
212220 // if has from but not to, to is the same as from (self-loop)
213221 if (from != null && to == null ) {
214222 to = from ;
215223 }
216224
217- // if from is !state, from is all states except state (multiple transitions)
218- if (from != null && from .startsWith ("!" )) {
219- String excludedState = from .substring (1 );
220- if (states != null && to != null ) {
221- for (String state : states ) {
222- if (!state .equals (excludedState )) {
223- transitions .add (new StateMachineTransition (state , to , methodName ));
224- }
225- }
226- }
227- return transitions ;
228- }
225+ // parse from and to expressions
226+ List <String > fromStates = parseStateExpression (from , states );
227+ List <String > toStates = parseStateExpression (to , states );
229228
230- // if has to but not from, from is all states (multiple transitions)
231- if (to != null && from == null ) {
232- if (states != null ) {
233- for (String state : states ) {
234- transitions .add (new StateMachineTransition (state , to , methodName ));
235- }
236- }
237- return transitions ;
229+ // if no from states, use all states
230+ if (fromStates .isEmpty () && to != null ) {
231+ fromStates = new ArrayList <>(states );
238232 }
239233
240- // normal transition
241- if (from != null && to != null ) {
242- transitions .add (new StateMachineTransition (from , to , methodName ));
234+ // create transitions for each combination of from and to states
235+ for (String fromState : fromStates ) {
236+ for (String toState : toStates ) {
237+ transitions .add (new StateMachineTransition (fromState , toState , method ));
238+ }
243239 }
244240 return transitions ;
245241 }
246242
247243 /**
248- * Normalizes the state value by removing (this) and returning null if empty
249- * @param value
250- * @return normalized state
244+ * Parses a state expression and returns the list of states
245+ * @param expr
246+ * @param states
247+ * @return list of states
251248 */
252- private static String normalizeState (Object value ) {
253- if (value == null ) return null ;
254- String normalized = value .toString ().replace ("(this)" , "" );
255- return normalized .isEmpty () ? null : normalized ;
249+ private static List <String > parseStateExpression (String expr , List <String > states ) {
250+ if (expr == null || expr .isEmpty ()) return new ArrayList <>();
251+ Expression ast = RefinementsParser .createAST (expr , "" );
252+ return getStateExpressions (ast , states );
253+ }
254+
255+ /**
256+ * Gets state names from an expression AST recursively
257+ * @param expr
258+ * @param states
259+ * @return list of states
260+ */
261+ private static List <String > getStateExpressions (Expression expr , List <String > states ) {
262+ List <String > stateExpressions = new ArrayList <>();
263+ if (expr instanceof Var var ) {
264+ stateExpressions .add (var .getName ());
265+ } else if (expr instanceof FunctionInvocation func ) {
266+ stateExpressions .add (func .getName ());
267+ } else if (expr instanceof GroupExpression group ) {
268+ stateExpressions .addAll (getStateExpressions (group .getExpression (), states ));
269+ } else if (expr instanceof BinaryExpression bin ) {
270+ String op = bin .getOperator ();
271+ if (op .equals ("||" )) {
272+ // combine states from both operands
273+ stateExpressions .addAll (getStateExpressions (bin .getFirstOperand (), states ));
274+ stateExpressions .addAll (getStateExpressions (bin .getSecondOperand (), states ));
275+ }
276+ } else if (expr instanceof UnaryExpression unary ) {
277+ if (unary .getOp ().equals ("!" )) {
278+ // all except those in the expression
279+ List <String > negatedStates = getStateExpressions (unary .getExpression (), states );
280+ for (String state : states ) {
281+ if (!negatedStates .contains (state )) {
282+ stateExpressions .add (state );
283+ }
284+ }
285+ }
286+ }
287+ return stateExpressions ;
256288 }
257289}
0 commit comments