PseudoCodeNode.java 15 KB

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