PseudoCodeNode.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. package animation;
  2. import javax.swing.JTree;
  3. import javax.swing.tree.DefaultMutableTreeNode;
  4. import javax.swing.tree.MutableTreeNode;
  5. import javax.swing.tree.TreePath;
  6. /**
  7. * represents a line of pseudocode
  8. * @author kolja
  9. *
  10. */
  11. public class PseudoCodeNode extends DefaultMutableTreeNode {
  12. public static enum CodeAction
  13. {
  14. SKIP,
  15. STOP,
  16. CONTINUE
  17. }
  18. public static enum CodeStatus
  19. {
  20. UNFINISHED,
  21. BREAKPOINT,
  22. FINISHED
  23. }
  24. private static final long serialVersionUID = 1L;
  25. private static int nextNodeId = 0;
  26. private final int nodeId;
  27. private AnimationController controller;
  28. private AnimatedAlgorithm alg;
  29. private JTree tree;
  30. private CodeLine code;
  31. private boolean selected;
  32. private boolean breakPoint;
  33. private boolean function;
  34. private int currentCodeLine; // next forward code line
  35. public PseudoCodeNode( String description, JTree tree, CodeLine line, AnimatedAlgorithm alg )
  36. {
  37. super( description );
  38. this.alg = alg;
  39. synchronized( PseudoCodeNode.class )
  40. {
  41. nodeId = nextNodeId++;
  42. }
  43. selected = false;
  44. this.tree = tree;
  45. breakPoint = false;
  46. currentCodeLine = -1;
  47. code = line;
  48. function = false;
  49. }
  50. public void setController( AnimationController c )
  51. {
  52. if( children != null )
  53. {
  54. for( Object ch : children )
  55. {
  56. ((PseudoCodeNode)ch).setController( c );
  57. }
  58. }
  59. controller = c;
  60. }
  61. public void writeToStack( Memory m )
  62. {
  63. m.declare( "_pos" + nodeId, currentCodeLine, false );
  64. m.declare( "_func" + nodeId, function, false );
  65. currentCodeLine = -1;
  66. function = false;
  67. setSelected( false );
  68. if( children == null )
  69. return;
  70. for( Object c : children )
  71. ((PseudoCodeNode)c).writeToStack( m );
  72. }
  73. public void loadFromStack( Memory m )
  74. {
  75. currentCodeLine = m.read( "_pos" + nodeId, false );
  76. function = m.read( "_func" + nodeId, false );
  77. if( children == null )
  78. return;
  79. for( Object c : children )
  80. ((PseudoCodeNode)c).loadFromStack( m );
  81. }
  82. @Override
  83. public void add( MutableTreeNode node )
  84. {
  85. ((PseudoCodeNode)node).setController( controller );
  86. super.add( node );
  87. }
  88. /**
  89. *
  90. * @return the tree that this node belongs to
  91. */
  92. public JTree getTree()
  93. {
  94. return tree;
  95. }
  96. /**
  97. * checks if this node should be highlighted
  98. * @return true if it should, false otherwise
  99. */
  100. public boolean isSelected()
  101. {
  102. return selected;
  103. }
  104. /**
  105. * checks if one of the subnodes of this node is selected.
  106. * @return true if one is, false otherwise
  107. */
  108. public boolean hasSelectedSubnode()
  109. {
  110. if( children != null )
  111. {
  112. for( Object ch : children )
  113. {
  114. if( ((PseudoCodeNode)ch).isSelected() || ((PseudoCodeNode)ch).hasSelectedSubnode() )
  115. return true;
  116. }
  117. }
  118. return false;
  119. }
  120. private void expandToRoot()
  121. {
  122. if( parent != null )
  123. ((PseudoCodeNode)parent).expandToRoot();
  124. tree.expandPath( new TreePath( this.getPath() ) );
  125. }
  126. /**
  127. * highlight this line of pseudocode.
  128. * should be called when the line is entered, as it triggers breakpoints
  129. * @param selected whether to select or deselect this line
  130. * @return false iff a breakpoint was reached and the node was set to be selected.
  131. */
  132. public CodeAction setSelected( boolean selected )
  133. {
  134. if( selected && breakPoint )
  135. controller.setContinuous( false );
  136. this.selected = selected;
  137. if( selected )
  138. {
  139. if( controller == null || controller.getStepOption() != 1 || breakPoint )
  140. expandToRoot();
  141. }
  142. else
  143. {
  144. if( controller == null || controller.getStepOption() != 1 )
  145. tree.collapsePath( new TreePath( this.getPath() ) );
  146. }
  147. if( breakPoint && selected )
  148. return CodeAction.STOP; // Breakpoint
  149. if( controller != null && controller.getStepOption() == 1 && !tree.isVisible( new TreePath( this.getPath() ) ) )
  150. return CodeAction.SKIP; // Step would be to detailed
  151. return CodeAction.CONTINUE; // Normal
  152. }
  153. /**
  154. * set a breakpoint at this line of code
  155. * @param breakPoint whether there should be a breakpoint or node
  156. */
  157. public void setBreakPoint( boolean breakPoint )
  158. {
  159. this.breakPoint = breakPoint;
  160. }
  161. /**
  162. * check if there is a breakpoint set at this line of code
  163. * @return true, iff there is a breakpoint
  164. */
  165. public boolean hasBreakPoint()
  166. {
  167. return breakPoint;
  168. }
  169. private PseudoCodeNode getForwardNode()
  170. {
  171. if( currentCodeLine == -1 )
  172. return this;
  173. if( children != null && children.size() > currentCodeLine )
  174. return ((PseudoCodeNode)children.get( currentCodeLine )).getForwardNode();
  175. return this;
  176. }
  177. private PseudoCodeNode getBackwardNode()
  178. {
  179. if( currentCodeLine - 1 <= -1 )
  180. return this;
  181. if( children != null && children.size() >= currentCodeLine - 1 )
  182. return ((PseudoCodeNode)children.get( currentCodeLine - 1 )).getBackwardNode();
  183. return this;
  184. }
  185. private CodeStatus stepInto( Memory m )
  186. {
  187. currentCodeLine = 0;
  188. if( children == null || children.size() == 0 )
  189. {
  190. setSelected( false );
  191. return CodeStatus.FINISHED;
  192. }
  193. else
  194. {
  195. setSelected( false );
  196. return selectChild( 0, m );
  197. }
  198. }
  199. /**
  200. * Perform one atomic step of the algorithm. Stops at the end of the program.
  201. * @return whether the whole stage is finished (afterwards).
  202. * For example if all steps are finished, then {@code FINISHED} is returned.
  203. */
  204. public CodeStatus forwardStep( Memory m )
  205. {
  206. if( currentCodeLine == -1 )
  207. {
  208. if( !m.isDefined( "node_" + nodeId + "_call", false ) )
  209. {
  210. StackFrame tmp = null;
  211. if( function )
  212. {
  213. tmp = m.removeFrame();
  214. m.addFrame( tmp ); // a little abuse of the stack to get direct access of the current frame before it could be destroyed by the user defined function
  215. }
  216. ControlFlow cf = code.runForward( m );
  217. switch( cf.getStatus() )
  218. {
  219. case ControlFlow.STEP_INTO:
  220. writeToStack( m );
  221. function = true;
  222. switch( stepInto( m ) )
  223. {
  224. case BREAKPOINT:
  225. hiddenActionWithoutSideEffects( m );
  226. return CodeStatus.BREAKPOINT;
  227. case FINISHED:
  228. currentCodeLine = -1;
  229. switch( setSelected( true ) )
  230. {
  231. case SKIP:
  232. hiddenActionWithoutSideEffects( m );
  233. return forwardStepOverIntern( m );
  234. case STOP:
  235. hiddenActionWithoutSideEffects( m );
  236. return CodeStatus.BREAKPOINT;
  237. default:
  238. break;
  239. }
  240. case UNFINISHED:
  241. hiddenActionWithoutSideEffects( m );
  242. return CodeStatus.UNFINISHED;
  243. }
  244. case ControlFlow.STEP_OVER:
  245. if( function )
  246. {
  247. m.addFrame( tmp ); // add old stack frame
  248. loadFromStack( m ); // load stored variables
  249. m.removeFrame(); // remove the stack frame
  250. setSelected( false );
  251. }
  252. hiddenActionWithoutSideEffects( m );
  253. return CodeStatus.FINISHED;
  254. case ControlFlow.CALL:
  255. alg.addActiveFunction( cf.getFunction() );
  256. m.declare( "node_" + nodeId + "_call", cf.getFunction(), false );
  257. setSelected( false );
  258. m.declare( "callback", this, true );
  259. switch( cf.getFunction().setSelected( true ) )
  260. {
  261. case CONTINUE:
  262. break;
  263. case SKIP:
  264. switch( cf.getFunction().forwardStepOverIntern( m ) )
  265. {
  266. case BREAKPOINT:
  267. hiddenActionWithoutSideEffects( m );
  268. return CodeStatus.BREAKPOINT;
  269. case FINISHED:
  270. setSelected( true );
  271. case UNFINISHED:
  272. hiddenActionWithoutSideEffects( m );
  273. return CodeStatus.UNFINISHED;
  274. }
  275. break;
  276. case STOP:
  277. hiddenActionWithoutSideEffects( m );
  278. return CodeStatus.BREAKPOINT;
  279. }
  280. hiddenActionWithoutSideEffects( m );
  281. return CodeStatus.UNFINISHED;
  282. }
  283. }
  284. else
  285. {
  286. m.undeclare( "node_" + nodeId + "_call", false );
  287. hiddenActionWithoutSideEffects( m );
  288. return CodeStatus.FINISHED;
  289. }
  290. }
  291. else
  292. {
  293. if( children == null || children.size() <= currentCodeLine )
  294. {
  295. throw new IllegalStateException( "Some wired stuff is going on" );
  296. }
  297. switch( ( (PseudoCodeNode)children.get( currentCodeLine ) ).forwardStep( m ) )
  298. {
  299. case BREAKPOINT:
  300. hiddenActionWithoutSideEffects( m );
  301. return CodeStatus.BREAKPOINT;
  302. case FINISHED:
  303. ( (PseudoCodeNode)children.get( currentCodeLine ) ).setSelected( false );
  304. currentCodeLine++;
  305. if( children.size() <= currentCodeLine )
  306. {
  307. currentCodeLine = -1;
  308. switch( setSelected( true ) )
  309. {
  310. case SKIP:
  311. hiddenActionWithoutSideEffects( m );
  312. return forwardStepOverIntern( m );
  313. case STOP:
  314. hiddenActionWithoutSideEffects( m );
  315. return CodeStatus.BREAKPOINT;
  316. default:
  317. break;
  318. }
  319. }
  320. else
  321. {
  322. CodeStatus status = selectChild( currentCodeLine, m );
  323. if( status == CodeStatus.FINISHED )
  324. {
  325. currentCodeLine = -1;
  326. status = CodeStatus.UNFINISHED;
  327. switch( setSelected( true ) )
  328. {
  329. case SKIP:
  330. hiddenActionWithoutSideEffects( m );
  331. return forwardStepOverIntern( m );
  332. case STOP:
  333. hiddenActionWithoutSideEffects( m );
  334. return CodeStatus.BREAKPOINT;
  335. default:
  336. break;
  337. }
  338. }
  339. hiddenActionWithoutSideEffects( m );
  340. return status;
  341. }
  342. case UNFINISHED:
  343. hiddenActionWithoutSideEffects( m );
  344. return CodeStatus.UNFINISHED;
  345. }
  346. }
  347. hiddenActionWithoutSideEffects( m );
  348. return CodeStatus.UNFINISHED;
  349. }
  350. private CodeStatus selectChild( int index, Memory m )
  351. {
  352. switch( ( (PseudoCodeNode)children.get( index ) ).setSelected( true ) )
  353. {
  354. case CONTINUE:
  355. return CodeStatus.UNFINISHED;
  356. case SKIP:
  357. switch( ( (PseudoCodeNode)children.get( index ) ).forwardStepOverIntern( m ) )
  358. {
  359. case BREAKPOINT:
  360. return CodeStatus.BREAKPOINT;
  361. case FINISHED:
  362. ( (PseudoCodeNode)children.get( index ) ).setSelected( false );
  363. currentCodeLine++;
  364. if( children == null || currentCodeLine >= children.size() )
  365. {
  366. return CodeStatus.FINISHED;
  367. }
  368. else
  369. {
  370. return selectChild( currentCodeLine, m );
  371. }
  372. case UNFINISHED:
  373. throw new IllegalStateException( "Skipping a node returned UNFINISHED" );
  374. }
  375. case STOP:
  376. return CodeStatus.BREAKPOINT;
  377. }
  378. return CodeStatus.UNFINISHED;
  379. }
  380. /**
  381. * Perform steps until the next line of code on the same level of indentation as this line
  382. * is reached. Stops at the end of the program.
  383. * @return whether the whole stage is finished (afterwards).
  384. * For example if all steps are finished, then {@code FINISHED} is returned.
  385. */
  386. public CodeStatus forwardStepOver( Memory m )
  387. {
  388. return getForwardNode().forwardStepOverIntern( m );
  389. }
  390. private CodeStatus forwardStepOverIntern( Memory m )
  391. {
  392. CodeStatus status = null;
  393. do {
  394. status = forwardStep( m );
  395. } while( status == CodeStatus.UNFINISHED );
  396. return status;
  397. }
  398. /**
  399. * Perform steps until the next line of code on the level of indentation above this lines
  400. * level is reached. Stops at the end of the program.
  401. * @return whether the whole stage is finished (afterwards).
  402. * For example if all steps are finished, then {@code FINISHED} is returned.
  403. */
  404. public CodeStatus forwardStepOut( Memory m )
  405. {
  406. return getForwardNode().forwardStepOutIntern( m );
  407. }
  408. private CodeStatus forwardStepOutIntern( Memory m )
  409. {
  410. if( parent != null )
  411. return ((PseudoCodeNode)parent).forwardStepOverIntern( m );
  412. return forwardStepOverIntern( m );
  413. }
  414. /**
  415. * Undo one atomic step of the algorithm. Stops at the beginning of the program.
  416. * @return whether the whole stage is finished in backwards direction (afterwards).
  417. * For example if all steps have been reverted, then {@code FINISHED} is returned.
  418. */
  419. public CodeStatus backwardStep( Memory m )
  420. {
  421. // TODO
  422. return null;
  423. }
  424. /**
  425. * Perform backward steps until the previous line of code on the same level of indentation
  426. * as this line is reached. Stops at the end of the program.
  427. * @return whether the whole stage is finished in backwards direction (afterwards).
  428. * For example if all steps have been reverted, then {@code FINISHED} is returned.
  429. */
  430. public CodeStatus backwardStepOver( Memory m )
  431. {
  432. return getBackwardNode().backwardStepOverIntern( m );
  433. }
  434. private CodeStatus backwardStepOverIntern( Memory m )
  435. {
  436. CodeStatus status = null;
  437. do {
  438. status = backwardStep( m );
  439. } while( status == CodeStatus.UNFINISHED );
  440. return status;
  441. }
  442. /**
  443. * Perform backward steps until the previous line of code on the level of indentation above
  444. * this lines level is reached. Stops at the end of the program.
  445. * @return whether the whole stage is finished in backwards direction (afterwards).
  446. * For example if all steps have been reverted, then {@code FINISHED} is returned.
  447. */
  448. public CodeStatus backwardStepOut( Memory m )
  449. {
  450. return getBackwardNode().backwardStepOutIntern( m );
  451. }
  452. private CodeStatus backwardStepOutIntern( Memory m )
  453. {
  454. if( parent != null )
  455. return ((PseudoCodeNode)parent).backwardStepOver( m );
  456. return backwardStepOutIntern( m );
  457. }
  458. protected void hiddenActionWithoutSideEffects( Memory m ) {}
  459. }