22
33import java .net .URI ;
44import java .util .ArrayList ;
5+ import java .util .Collection ;
56import java .util .HashSet ;
67import java .util .List ;
78import java .util .Set ;
1314import spoon .reflect .CtModel ;
1415import spoon .reflect .declaration .CtAnnotation ;
1516import spoon .reflect .declaration .CtClass ;
16- import spoon .reflect .declaration .CtConstructor ;
17- import spoon .reflect .declaration .CtInterface ;
17+ import spoon .reflect .declaration .CtElement ;
1818import spoon .reflect .declaration .CtMethod ;
1919import spoon .reflect .declaration .CtType ;
2020
@@ -41,32 +41,21 @@ public static StateMachine parse(String uri) {
4141
4242 // get class or interface
4343 CtType <?> ctType = getType (model );
44- if (ctType == null ) {
45- return null ;
46- }
44+ if (ctType == null )
45+ return null ; // no class or interface found
4746
4847 // extract class name and states
4948 List <String > states = getStates (ctType );
50- if (states == null || states .isEmpty ()) {
51- return null ;
52- }
53-
49+ if (states == null || states .isEmpty ())
50+ return null ; // no states found
5451 String className = getClassName (ctType );
5552
56- // extract initial state and transitions
57- List <String > initialStates ;
58- List <StateMachineTransition > transitions ;
59- if (ctType instanceof CtClass <?> ctClass ) {
60- initialStates = getInitialStatesFromClass (ctClass , states );
61- transitions = getTransitionsFromClass (ctClass , states );
62- } else if (ctType instanceof CtInterface <?> ctInterface ) {
63- initialStates = getInitialStatesFromInterface (ctInterface , className , states );
64- transitions = getTransitionsFromInterface (ctInterface , className , states );
65- } else {
66- return null ;
67- }
68- if (transitions .isEmpty ()) return null ; // no transitions found
69-
53+ // get initial states and transitions
54+ List <String > initialStates = getInitialStates (ctType , className , states );
55+ List <StateMachineTransition > transitions = getTransitions (ctType , className , states );
56+ if (transitions .isEmpty ())
57+ return null ; // no transitions found
58+
7059 return new StateMachine (className , initialStates , states , transitions );
7160
7261 } catch (Exception e ) {
@@ -82,9 +71,8 @@ public static StateMachine parse(String uri) {
8271 */
8372 private static CtType <?> getType (CtModel model ) {
8473 for (CtType <?> type : model .getAllTypes ()) {
85- if (type instanceof CtClass <?> || type instanceof CtInterface <?>) {
74+ if (type . isClass () || type . isInterface ())
8675 return type ;
87- }
8876 }
8977 return null ;
9078 }
@@ -99,7 +87,7 @@ private static String getClassName(CtType<?> ctType) {
9987 for (CtAnnotation <?> annotation : ctType .getAnnotations ()) {
10088 if (annotation .getAnnotationType ().getSimpleName ().equals (EXTERNAL_REFINEMENTS_FOR_ANNOTATION )) {
10189 String qualifiedName = (String ) annotation .getValueAsObject ("value" );
102- return Utils .getSimpleName (qualifiedName );
90+ return Utils .getSimpleName (qualifiedName );
10391 }
10492 }
10593 return ctType .getSimpleName ();
@@ -121,80 +109,54 @@ private static List<String> getStates(CtType<?> ctType) {
121109 }
122110
123111 /**
124- * Gets the initial states from a class
125- * If not explicitely defined, uses the first state in the state set
126- * @param ctClass the CtClass
127- * @param states the list of states
128- * @return initial states
112+ * Gets the elements that represent constructors (actual constructors for classes, methods named after the class for interfaces)
113+ * @param ctType the CtType (class or interface)
114+ * @param className the class name
115+ * @return collection of constructor elements
129116 */
130- private static List <String > getInitialStatesFromClass (CtClass <?> ctClass , List <String > states ) {
131- Set <String > initialStates = new HashSet <>();
132- for (CtConstructor <?> constructor : ctClass .getConstructors ()) {
133- for (CtAnnotation <?> annotation : constructor .getAnnotations ()) {
134- if (annotation .getAnnotationType ().getSimpleName ().equals (STATE_REFINEMENT_ANNOTATION )) {
135- String to = annotation .getValueAsString ("to" );
136- List <String > parsedStates = parseStateExpression (to , states );
137- initialStates .addAll (parsedStates );
138- }
139- }
117+ private static Collection <? extends CtElement > getConstructorElements (CtType <?> ctType , String className ) {
118+ if (ctType instanceof CtClass <?> ctClass ) {
119+ return ctClass .getConstructors ();
140120 }
141- return initialStates .isEmpty () ? List .of (states .get (0 )) : initialStates .stream ().toList ();
121+ // for interfaces the constructors are methods with the same name as the class
122+ return ctType .getMethods ().stream ().filter (m -> m .getSimpleName ().equals (className )).toList ();
142123 }
143124
144125 /**
145- * Gets the initial state from an interface
146- * If not explicitely defined, uses the first state in the state set
147- * @param ctInterface the CtInterface
126+ * Gets the initial states from a class or interface
127+ * If not explicitly defined, uses the first state in the state set
128+ * @param ctType the CtType (class or interface)
148129 * @param className the class name
130+ * @param states the list of states
149131 * @return initial states
150132 */
151- private static List <String > getInitialStatesFromInterface ( CtInterface <?> ctInterface , String className , List <String > states ) {
133+ private static List <String > getInitialStates ( CtType <?> ctType , String className , List <String > states ) {
152134 Set <String > initialStates = new HashSet <>();
153- for (CtMethod <?> method : ctInterface .getMethods ()) {
154- if (method .getSimpleName ().equals (className )) {
155- for (CtAnnotation <?> annotation : method .getAnnotations ()) {
156- if (annotation .getAnnotationType ().getSimpleName ().equals (STATE_REFINEMENT_ANNOTATION )) {
157- String to = annotation .getValueAsString ("to" );
158- List <String > parsedStates = parseStateExpression (to , states );
159- initialStates .addAll (parsedStates );
160- }
161- }
162- }
163- }
164- return initialStates .isEmpty () ? List .of (states .get (0 )) : initialStates .stream ().toList ();
165- }
166-
167- /**
168- * Gets transitions from a class
169- * @param ctClass the CtClass
170- * @param states the list of states
171- * @return list of StateMachineTransition
172- */
173- private static List <StateMachineTransition > getTransitionsFromClass (CtClass <?> ctClass , List <String > states ) {
174- List <StateMachineTransition > transitions = new ArrayList <>();
175- for (CtMethod <?> method : ctClass .getMethods ()) {
176- for (CtAnnotation <?> annotation : method .getAnnotations ()) {
135+ for (CtElement element : getConstructorElements (ctType , className )) {
136+ for (CtAnnotation <?> annotation : element .getAnnotations ()) {
177137 if (annotation .getAnnotationType ().getSimpleName ().equals (STATE_REFINEMENT_ANNOTATION )) {
178- List <StateMachineTransition > extracted = getTransitions (annotation , method .getSimpleName (), states );
179- transitions .addAll (extracted );
138+ String to = annotation .getValueAsString ("to" );
139+ List <String > parsedStates = parseStateExpression (to , states );
140+ initialStates .addAll (parsedStates );
180141 }
181142 }
182143 }
183-
184- return transitions ;
144+ return initialStates .isEmpty () ? List .of (states .get (0 )) : initialStates .stream ().toList ();
185145 }
186146
187147 /**
188- * Gets transitions from an interface
189- * @param ctInterface the CtInterface
148+ * Gets transitions from a class or interface
149+ * @param ctType the CtType (class or interface)
190150 * @param className the class name
191151 * @param states the list of states
192152 * @return list of StateMachineTransition
193153 */
194- private static List <StateMachineTransition > getTransitionsFromInterface ( CtInterface <?> ctInterface , String className , List <String > states ) {
154+ private static List <StateMachineTransition > getTransitions ( CtType <?> ctType , String className , List <String > states ) {
195155 List <StateMachineTransition > transitions = new ArrayList <>();
196- for (CtMethod <?> method : ctInterface .getMethods ()) {
197- if (method .getSimpleName ().equals (className )) continue ; // skip constructor method
156+ for (CtMethod <?> method : ctType .getMethods ()) {
157+ // for interfaces we skip constructor methods (methods with same name as class)
158+ if (ctType .isInterface () && method .getSimpleName ().equals (className ))
159+ continue ;
198160
199161 for (CtAnnotation <?> annotation : method .getAnnotations ()) {
200162 if (annotation .getAnnotationType ().getSimpleName ().equals (STATE_REFINEMENT_ANNOTATION )) {
@@ -261,36 +223,34 @@ private static List<String> parseStateExpression(String expr, List<String> state
261223 */
262224 private static List <String > getStateExpressions (Expression expr , List <String > states ) {
263225 List <String > stateExpressions = new ArrayList <>();
264- switch (expr ) {
265- case Var var -> stateExpressions .add (var .getName ());
266- case FunctionInvocation func -> stateExpressions .add (func .getName ());
267- case GroupExpression group -> stateExpressions .addAll (getStateExpressions (group .getExpression (), states ));
268- case BinaryExpression bin -> {
269- String op = bin .getOperator ();
270- if (op .equals ("||" )) {
271- // combine states from both operands
272- stateExpressions .addAll (getStateExpressions (bin .getFirstOperand (), states ));
273- stateExpressions .addAll (getStateExpressions (bin .getSecondOperand (), states ));
274- }
226+ if (expr instanceof Var var ) {
227+ stateExpressions .add (var .getName ());
228+ } else if (expr instanceof FunctionInvocation func ) {
229+ stateExpressions .add (func .getName ());
230+ } else if (expr instanceof GroupExpression group ) {
231+ stateExpressions .addAll (getStateExpressions (group .getExpression (), states ));
232+ } else if (expr instanceof BinaryExpression bin ) {
233+ String op = bin .getOperator ();
234+ if (op .equals ("||" )) {
235+ // combine states from both operands
236+ stateExpressions .addAll (getStateExpressions (bin .getFirstOperand (), states ));
237+ stateExpressions .addAll (getStateExpressions (bin .getSecondOperand (), states ));
275238 }
276- case 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- }
239+ } else if (expr instanceof UnaryExpression unary ) {
240+ if (unary .getOp ().equals ("!" )) {
241+ // all except those in the expression
242+ List <String > negatedStates = getStateExpressions (unary .getExpression (), states );
243+ for (String state : states ) {
244+ if (!negatedStates .contains (state )) {
245+ stateExpressions .add (state );
284246 }
285247 }
286248 }
287- case Ite ite -> {
288- // combine states from then and else branches
289- // TODO: handle conditional transitions
290- stateExpressions .addAll (getStateExpressions (ite .getThen (), states ));
291- stateExpressions .addAll (getStateExpressions (ite .getElse (), states ));
292- }
293- default -> {}
249+ } else if (expr instanceof Ite ite ) {
250+ // combine states from then and else branches
251+ // TODO: handle conditional transitions
252+ stateExpressions .addAll (getStateExpressions (ite .getThen (), states ));
253+ stateExpressions .addAll (getStateExpressions (ite .getElse (), states ));
294254 }
295255 return stateExpressions ;
296256 }
0 commit comments