Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

OTExample Stopwatch

Idea.png
Try it live:
In a running OTDT you can easily import this project into your workspace using this menu path:
File > New > Example > Stop Watch Example


The Stopwatch example shows the most simple Model View Controller architecture using OT/J.

This example consists basically of two classes:

1. Stopwatch (Model)

public class StopWatch implements Runnable {

        /** Current time value */
        private int time = 0;

        /** Flag that indicates if the watch is running */
        private boolean running;

        /** Flag that indicates if there is a stop request */
        private boolean stopRequest;

        /** Increases the time value. */
        private synchronized void advance() {
                time++;
        }

        /** Starts the watch thread. */
        public synchronized void start() {
                if (!running) {
                        setRunning(true);
                        setStopped(false);
                        (new Thread(this)).start();
                }
        }

        /** Stops the watch thread. */
        public synchronized void stop() {
                setStopped(true);
        }

        /** Resets the time value. */
        public synchronized void reset() {
                time = 0;
        }

        /** Returns the current time value. */
        public synchronized int getValue() {
                return time;
        }

        /** Sets the running flag value. */
        private synchronized void setRunning(boolean value) {
                running = value;
        }

        /** Returns the stop flag value */
        private synchronized boolean isStopped() {
                return stopRequest;
        }

        /** Sets the stop flag value */
        private synchronized void setStopped(boolean value) {
                stopRequest = value;
        }

        /** This method contains the main loop that is executed while the watch is running. */
        public void run() {
                while (true) {
                        try {
                                Thread.sleep(1000);
                        } catch (InterruptedException e) {
                                setRunning(false);
                                return;
                        }
                        if (isStopped()) {
                                setRunning(false);
                                return;
                        }
                        advance();
                }
        }
}

This pure Java class only implements the functionality of a stopwatch with no thought whatsoever about display of any kind.

2. Now for class WatchUI (combined View and Controller):

  1. public team class WatchUI {
  2.     static int ypos = 150;
  3.  
  4.     /**
  5.      * Role class WatchDisplay is played by the base class StopWatch. The role
  6.      * class WatchDisplay is bound to the base class StopWatch with the keyowrd
  7.      * 'playedBy'.
  8.      */
  9.     protected class WatchDisplay extends JFrame playedBy StopWatch 
  10.     {
  11. 	private JTextField display;
  12. 	private JButton startButton;
  13. 	private JButton stopButton;
  14. 	private JButton clearButton;
  15.  
  16. 	/**
  17. 	 * This constructor is used for automatic role creation. E.g. via
  18. 	 * declared lifting. Role class constructor takes an object of the type
  19. 	 * of the declared base class. Setup the window, create a textfield for
  20. 	 * time display and three buttons "start", "stop", and "clear".
  21. 	 */
  22. 	public WatchDisplay(StopWatch w) {
  23. 		setTitle("Digital Stop Watch");
  24. 		setSize(new Dimension(300, 100));
  25. 		setLocation(410, ypos+=110);
  26. 		Container pane = getContentPane();
  27. 		pane.setLayout(new GridLayout(2,3));
  28. 		pane.add(new JLabel(""));
  29. 		display = new JTextField("0", 8);
  30. 		display.setHorizontalAlignment(JTextField.RIGHT);
  31. 		pane.add(display);
  32. 		pane.add(new JLabel(""));
  33. 		startButton = new JButton("start");
  34. 		startButton.addActionListener(new ActionListener() {
  35. 			public void actionPerformed(ActionEvent e) {
  36. 				start();
  37. 			}});
  38. 		pane.add(startButton);
  39. 		stopButton = new JButton("stop");
  40. 		stopButton.addActionListener(new ActionListener() {
  41. 			public void actionPerformed(ActionEvent e) {
  42. 				stop();
  43. 			}});
  44. 		pane.add(stopButton);
  45. 		clearButton = new JButton("clear");
  46. 		clearButton.addActionListener(new ActionListener() {
  47. 			public void actionPerformed(ActionEvent e) {
  48. 				clear();
  49. 			}});
  50. 		pane.add(clearButton);
  51. 		setVisible(true);
  52. 	}		
  53.  
  54. 	/**
  55. 	 * Shows the new time value on the watch display.
  56. 	 */
  57. 	void update() {
  58. 	    String val = getStringValue();
  59. 	    display.setText(val);
  60. 	}
  61.  
  62. 	// Abstract methods for mapping to the concrete base methods:
  63. 	abstract void   start();
  64. 	abstract void   stop();
  65. 	abstract void   clear();
  66. 	abstract String getStringValue();
  67.  
  68.        // callout method bindings: any call of the abstract WatchDisplay
  69.        // method will be forwarded to the concrete StopWatch method
  70. 	       start            ->       start;
  71. 	       stop             ->       stop;
  72. 	       clear            ->       reset;
  73. 	String getStringValue()	-> int   getValue() 
  74. 	    with {
  75. 		    // result is a predefined name.
  76. 		    result      <-   Integer.toString(result)
  77. 	    }
  78.  
  79. 	/* -------------------------------------------------------------- */
  80.  
  81. 	// Callin method bindings: WatchDisplay (role object) is updated
  82. 	// after the StopWatch (base object) advanced or was reset. 
  83. 	   void update()	<- after void advance();
  84. 	   void update()        <- after void reset();
  85.     }
  86.  
  87.     /**
  88.      * The team constructor uses declared lifting. A WatchDisplay role is
  89.      * created for the given StopWatch object.
  90.      */
  91.     public WatchUI (StopWatch as WatchDisplay w) {
  92.     	activate(ALL_THREADS); // Without this, the callin bindings have no effect.
  93.     }
  94. }

What you see in this class:

(Links like OTJLD §0 refer to the corresponding paragraph in the OT/J Language Definition (OTJLD))

  • It's a team class (line 1) — OTJLD §1.1
  • It declares a role WatchDisplay extends JFrame playedBy StopWatch (l.9) — OTJLD §2.1
  • The role's constructor uses normal Swing programming to create a little UI (ll.22-52)
    • The buttons in this UI are connected to methods start,stop,clear (ll.36,42,48)
    • These methods are declared abstractly (ll.63-65)
  • A little update method sets the display text to whatever the abstract method getStringValue returns (ll.57-60,66)
  • The UI is connected to the Model only be a set of method bindings:
    • callout method bindings make corresponding methods of StopWatch accessible within WatchDisplay (ll.68-77) — OTJLD §3
    • thanks to these bindings these methods are no longer abstract
      • clear and getStringValue are mapped to method with different names in the base class (ll.72,73)
      • the parameter mapping for getStringValue additionally converts the provided int into a String (ll.74-77) — OTJLD §3.2
    • callin method bindings set the triggers when the display should be updated (ll.81-84) — OTJLD §4
  • The team's constructor receives a base object (StopWatch) which is automatically lifted to its WatchDisplay role before method entry (keyword as) (l.91) — OTJLD §2.3.2
    • The lifting mechanism invokes the lifting constructor (l.22) which puts up the UI — OTJLD §2.3.1.c
    • The constructor furthermore activates the team so that it indeed receives triggers via its callin bindings. — OTJLD §5

Alternatives

For some of the above code other options exist for expressing the same:

  • We already see callout bindings with and without method signatures (OTJLD §3.1.c). Signatures are mandatory when overloaded base methods exist or when using parameter mappings.
  • When a callout binding with signature is used the abstract method declaration can be omitted ("shorthand definition" OTJLD §3.1.i). E.g., line 66 above is unnecessary.
  • Also callin bindings may omit method signatures if no overloading is involved.
  • Instead of defining several separate callin bindings for the same role method (ll. 83f) a single binding may list several base methods separated by a comma (OTJLD §4.1.d).

3. The program is started using this tiny Main:

public class Main {
    public static void main(String[] args) {
	StopWatch w = new StopWatch();
	new WatchUI(w);
    }
}

We create both a model and a view, passing the model to the view where it will be decorated with a role, which in turn opens the UI.

The version shipped with the OTDT additionally bring an AnalogWatch in order to show how different views are connected to the same model. However, no further OT/J constructs or patterns are used for this.

Back to the top