<
From version < 29.2 >
edited by cds
on 2023/07/13 12:14
To version < 15.1 >
edited by cds
on 2012/10/24 12:13
>
Change comment: There is no comment for this version

Summary

Details

Page properties
Parent
... ... @@ -1,1 +1,0 @@
1 -WS12EclPract.Eclipse Practical (winter term 201213).Tutorials.WebHome
Content
... ... @@ -1,11 +1,11 @@
1 -This tutorial will teach you the basics of writing plugins that run inside the Eclipse framework. You will learn about editors, views, and extension points by creating one of each yourself. Once you're done with this tutorial, you will have an application that will look something like this:
1 +{{warning title="Warning"}}
2 +This tutorial isn't complete yet!
3 +{{/warning}}
2 2  
3 -[[image:attach:result.png]]
5 +This tutorial will teach you the basics of writing plugins that run inside the Eclipse framework. You will learn about editors, views, and extension points by creating one of each yourself.
4 4  
5 -You may want to download [[the slides>>attach:presentation.pdf]] of the presentation explaining the basic concepts you will explore in this tutorial.
6 6  
7 7  
8 -
9 9  {{toc/}}
10 10  
11 11  = Preliminaries =
... ... @@ -23,7 +23,7 @@
23 23  
24 24  Over the course of this tutorial, you will be writing a bit of code. Here's a few rules we ask you to follow:
25 25  
26 -* All your Java code should be in packages with the prefix {{code language="none"}}de.cau.cs.rtprak.login{{/code}}, where {{code language="none"}}login{{/code}} is your login name as used for your email address at the institute. From now on, this rule will apply to all tutorials. Once we start with the actual practical projects, we will choose another package name.
26 +* All your Java code should be in packages with the prefix {{code language="none"}}de.cau.cs.rtprakt.login{{/code}}, where {{code language="none"}}login{{/code}} is your login name as used for your email address at the institute. From now on, this rule will apply to all tutorials. Once we start with the actual practical projects, we will choose another package name.
27 27  * All Java classes, fields, and methods should be thoroughly commented with the standard [[Javadoc>>url:http://download.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#javadoctags||shape="rect"]] comment format. Javadoc comments are well supported by Eclipse, providing code completion, syntax highlighting, and further features to help you. The code inside your methods should also be well commented. Try to think about what kinds of information would help someone unfamiliar with your code understand it.
28 28  * As you will already have noticed during the first tutorial, our tutorials use Turing machines as the underlying theme. This is partly because we're computer scientists and computer scientists are expected to choose computer sciency examples, but mostly because Turing machines work great as examples for the different kinds of topics we will be covering with you. You may thus want to take some time to read up again on the topic. [[Wikipedia>>url:http://en.wikipedia.org/wiki/Turing_machine||shape="rect"]] or the material of your Theoretical Computer Science lecture might be a great start.
29 29  * During this tutorial, we will be using Git mostly from the command line instead of using Eclipse's built-in Git support. This is because we've found Eclipse's Git support to be too unstable and buggy for us to trust it completely.
... ... @@ -84,7 +84,7 @@
84 84  
85 85  1. //New// -> //Project...//
86 86  1. In the project wizard, choose //Plug-in Project// and click //Next//.
87 -1. As the project name, enter {{code language="none"}}de.cau.cs.rtprak.login.simple{{/code}}. Uncheck //Use default location// (which would put the project into your workspace), and put it into your local clone of the Git repository instead (the //Location// should read something like {{code language="none"}}/path/to/git/repository/de.cau.cs.rtprak.login.simple{{/code}}). Click //Next//.
87 +1. As the project name, enter {{code language="none"}}de.cau.cs.rtprakt.login.simple{{/code}}. Uncheck //Use default location// (which would put the project into your workspace), and put it into your local clone of the Git repository instead (the //Location// should read something like {{code language="none"}}/path/to/git/repository/de.cau.cs.rtprakt.login.simple{{/code}}). Click //Next//.
88 88  1. As the name, enter {{code language="none"}}Simple (login){{/code}}. Also, make sure that //Generate an activator// and //This plug-in will make contributions to the UI// are both checked. Click //Finish//. (Eclipse might ask you whether you want to switch to the //Plug-in Development Perspective//, which configures Eclipse to provide the views that are important for plug-in development. Choose //Yes//. Or //No//. It won't have a big influence on your future...)
89 89  1. Eclipse has now created your new plug-in and was nice enough to open the //Plug-in Manifest Editor//, which allows you to graphically edit two important files of your plugin: {{code language="none"}}plugin.xml{{/code}} and {{code language="none"}}META-INF/MANIFEST.MF{{/code}}. (By the way, this would be a great time to research the editor and the two files online.) Basically, those two files provide information that tell Eclipse what other plug-ins your plug-in needs and how it works together with other plug-ins by providing extensions and extension points. Our new plug-in will depend on two other plug-ins, so switch to the //Dependencies// tab of the editor and add dependencies to {{code language="none"}}org.eclipse.ui.editors{{/code}} and {{code language="none"}}org.eclipse.jface.text{{/code}}. Save the editor and close it. (You can always reopen it by opening one of the two mentioned files from the //Package Explorer//.)
90 90  1. Tell Eclipse that the project is inside a Git repository. Right-click on the project, click //Team//, and click //Share Project//. Select Git as the repository type and click //Next//. The repository information should appear and you should be able to simply click //Finish//.
... ... @@ -94,7 +94,7 @@
94 94  We will now create the class that implements the simple text editor. There won't be any programming involved here since we're lazy; instead, we will just inherit from an existing simple text editor.
95 95  
96 96  1. //New// -> //Class//.
97 -1. Package: {{code language="none"}}de.cau.cs.rtprak.login.simple.editors{{/code}}. Name: {{code language="none"}}SimpleEditorPart{{/code}}. Superclass: {{code language="none"}}org.eclipse.ui.editors.text.TextEditor{{/code}}. Click //Finish//.
97 +1. Package: {{code language="none"}}de.cau.cs.rtprakt.login.simple.editors{{/code}}. Name: {{code language="none"}}SimpleEditorPart{{/code}}. Superclass: {{code language="none"}}org.eclipse.ui.editors.text.TextEditor{{/code}}. Click //Finish//.
98 98  
99 99  == Register the Editor ==
100 100  
... ... @@ -103,7 +103,7 @@
103 103  1. Copy [[the attached file>>attach:turing-file.gif]] to a new subfolder icons in the plug-in folder (right-click the plug-in folder in the //Package Explorer// and choose //New// -> //Folder...//). You can copy the file by importing it from inside Eclipse (//File// -> //Import...// -> //File System//) or by copying it from outside Eclipse and refreshing the plug-in project afterwards (right-click the plug-in folder in the //Package Explorer// and choose //Refresh).//
104 104  1. Open the //Plug-in Manifest Editor// again and switch to the //Extensions// tab.
105 105  1. Click //Add...//, choose the {{code language="none"}}org.eclipse.ui.editors{{/code}} extension point and click //Finish//.
106 -1. The extension point is now shown in the list of extensions, along with an //editor// extension. Select that extension and edit its details using the fields on the right. Set the ID to {{code language="none"}}de.cau.cs.rtprak.login.simple.editor{{/code}}, the name to {{code language="none"}}Simple Text Editor{{/code}}, the icon to {{code language="none"}}icons/turing-file.gif{{/code}}, the extensions to {{code language="none"}}simple{{/code}}, the class to {{code language="none"}}de.cau.cs.rtprak.login.simple.editors.SimpleEditorPart{{/code}}, and the default to {{code language="none"}}true{{/code}}.
106 +1. The extension point is now shown in the list of extensions, along with an //editor// extension. Select that extension and edit its details using the fields on the right. Set the ID to {{code language="none"}}de.cau.cs.rtprakt.login.simple.editor{{/code}}, the name to {{code language="none"}}Simple Text Editor{{/code}}, the icon to {{code language="none"}}icons/turing-file.gif{{/code}}, the extensions to {{code language="none"}}simple{{/code}}, the class to {{code language="none"}}de.cau.cs.rtprakt.login.simple.editors.SimpleEditorPart{{/code}}, and the default to {{code language="none"}}true{{/code}}.
107 107  1. Save the manifest editor.
108 108  
109 109  == Test the Editor ==
... ... @@ -125,7 +125,7 @@
125 125  The next task consists of creating a view that is able to display the state of a Turing Machine. We will do this using a table with one column, where each row represents an entry on the tape of the Turing Machine. The tape shall be infinite to one side, and the position of the read/write head shall be movable by two buttons. The content of the tape shall be determined by the currently active instance of our simple text editor.
126 126  
127 127  {{info title="Hint"}}
128 -In the following, we will be making use of the Standard Widget Toolkit (SWT) and JFace to build a user interface. It might be a good idea now to search for an introduction to SWT and JFace concepts on the Internet before you proceed.
128 +In this tutorial, we will be making use of the Standard Widget Toolkit (SWT) to build a user interface. It might be a good idea now to search for an introduction to SWT concepts on the Internet now before you proceed.
129 129  {{/info}}
130 130  
131 131  == Creating the View Class ==
... ... @@ -132,471 +132,42 @@
132 132  
133 133  We will start by creating a class that will define the view.
134 134  
135 -1. Create a class {{code language="none"}}TapeViewPart{{/code}} in a new package {{code language="none"}}de.cau.cs.rtprak.login.simple.views{{/code}} that extends the [[ViewPart>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/part/ViewPart.html||shape="rect"]] class. (make sure that in the //New Java Class// wizard, the option //Inherited abstract methods// is checked.)
135 +1. Create a class {{code language="none"}}TapeViewPart{{/code}} in a new package {{code language="none"}}de.cau.cs.rtprakt.login.simple.views{{/code}} that extends the [[ViewPart>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/part/ViewPart.html||shape="rect"]] class.
136 136  1. Add a private field {{code language="none"}}tableViewer{{/code}} of type [[TableViewer>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/TableViewer.html||shape="rect"]].
137 137  1. (((
138 -Your {{code language="none"}}TableViewPart{{/code}} contains a still empty method {{code language="none"}}createPartControl{{/code}}. This method will be responsible for creating the user interface components of your view. Add the following code to create the table we want to display:
138 +Your TableViewPart contains a still empty method createPartControl. This method will be responsible for creating the user interface components of your view. Add the following code to create the table we want to display:
139 139  
140 -{{code language="java"}}
140 +{{code title="createPartControl(...)" linenumbers="true" language="java" collapse="true"}}
141 141  Table table = new Table(parent, SWT.BORDER);
142 -table.setHeaderVisible(true);
143 143  TableColumn column = new TableColumn(table, SWT.NONE);
144 144  column.setWidth(80);
145 -column.setText("Tape Data");
146 146  tableViewer = new TableViewer(table);
147 147  {{/code}}
148 148  )))
149 149  1. (((
150 -The {{code language="none"}}setFocus{{/code}} method controls what happens when your part gets the focus. Make sure the focus will then automatically be set to the table by adding the following code:
148 +The setFocus method controls what happens when your part gets the focus. Make sure the focus will then automatically be set to the table by adding the following code:
151 151  
152 -{{code}}
150 +{{code title="setFocus(...)" linenumbers="true" collapse="true"}}
153 153  tableViewer.getControl().setFocus();
154 154  {{/code}}
155 155  )))
156 156  
157 -== Create the View Extension ==
155 +
158 158  
159 -We will now have to register our new view with Eclipse so that it can be seamlessly integrated into the workbench.
157 +
160 160  
161 -1. Copy the three files [[attach:tape_head.gif]], [[attach:head_present.gif]], and [[attach:head_absent.gif]]to the {{code language="none"}}icons{{/code}} subfolder of your plug-in as you did it before. (You might need to refresh your project again if you did the copying outside of Eclipse.)
162 -1. Open the {{code language="none"}}plugin.xml{{/code}} file in the //Plugin Manifest Editor// and switch to the //Extensions// tab.
163 -1. Click //Add// to add a new extension for the extension point {{code language="none"}}org.eclipse.ui.views{{/code}}. Right-click the newly added extension and add a new {{code language="none"}}view{{/code}} element through the //New// menu.
164 -1. Set the view element's properties as follows: ID {{code language="none"}}de.cau.cs.rtprak.login.simple.view{{/code}}, name {{code language="none"}}Tape{{/code}}, class {{code language="none"}}de.cau.cs.rtprak.login.simple.views.TapeViewPart{{/code}}, category {{code language="none"}}org.eclipse.ui{{/code}}, icon {{code language="none"}}icons/tape_head.gif{{/code}}.
159 +
165 165  
166 -When you start the application, you should now be able to open your view by clicking //Window// -> //Show View// -> //Other//.
161 +
167 167  
168 -== Add Content and Label Providers ==
169 -
170 -The idea of JFace viewers is to abstract a bit from the underlying widget (in our case, the table) and instead work on data models that are to be viewed. Instead of adding items to the table directly, the table viewer is supplied with an input object, a content provider, and a label provider. The content provider allows the viewer to make sense of the input object and basically allows the viewer to access the input object's data. The label provider translates each item of data into text and icons that can be used to present the item to the user in the table.
171 -
172 -We will now create content and label providers to do just that.
173 -
174 -1. (((
175 -Create a class {{code language="none"}}TuringTape{{/code}} in a new package {{code language="none"}}de.cau.cs.rtprak.login.simple.model{{/code}} with the following fields:
176 -
177 -{{code language="java"}}
178 -private int headPosition = 1;
179 -private StringBuffer text = new StringBuffer();
180 -{{/code}}
181 -
182 -Also add corresponding getter and setter methods. (You can simply right-click somewhere in the class and choose //Source// -> //Generate Getters and Setters//.)
183 -)))
184 -1. (((
185 -Add two constants to the class:
186 -
187 -{{code language="java"}}
188 -public static final char START_CHAR = '\u25b7';
189 -public static final char BLANK_CHAR = '\u25fb';
190 -{{/code}}
191 -)))
192 -1. Add a method {{code language="none"}}getCharacter(int pos){{/code}} that calculates the tape character at position {{code language="none"}}pos{{/code}} as follows:\\
193 -1*. For {{code language="none"}}pos == 0{{/code}}, return the character {{code language="none"}}START_CHAR{{/code}}.
194 -1*. For {{code language="none"}}pos > text.length(){{/code}}, return the character {{code language="none"}}BLANK_CHAR{{/code}}.
195 -1*. Otherwise, return the text character at index {{code language="none"}}pos - 1{{/code}}.
196 -1. Add a private field tape of type {{code language="none"}}TuringTape{{/code}} to {{code language="none"}}TapeViewPart{{/code}} and initialize it with a new instance.
197 -1. Create a class {{code language="none"}}TapeData{{/code}} in {{code language="none"}}de.cau.cs.rtprak.login.simple.model{{/code}} with two fields {{code language="none"}}int index{{/code}} and {{code language="none"}}char character{{/code}}, and add a constructor for initialization as well as corresponding getter methods.
198 -1. Create a class {{code language="none"}}TapeContentProvider{{/code}} in the {{code language="none"}}de.cau.cs.rtprak.login.simple.views{{/code}} package that implements [[IStructuredContentProvider>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/IStructuredContentProvider.html||shape="rect"]].\\
199 -1*. The methods {{code language="none"}}dispose(){{/code}} and {{code language="none"}}inputChanged(){{/code}} may remain empty.
200 -1*. The method {{code language="none"}}getElements(){{/code}} must return an array of objects, where each object must contain all necessary data to be displayed in a single row of the table. The number of returned objects corresponds to the number of rows.
201 -1*. Suppose the input element is an instance of {{code language="none"}}TuringTape{{/code}}. The result of {{code language="none"}}getElements(){{/code}} shall be an array of {{code language="none"}}TapeData{{/code}} elements. The size of the array shall be one more than the maximum of the tape head position and the length of the tape text. The index and character of each tape data element shall be filled with {{code language="none"}}i{{/code}} and the result of {{code language="none"}}turingTape.getCharacter(i){{/code}}, respectively, where {{code language="none"}}i{{/code}} is the array index of the element.
202 -1. Create a class {{code language="none"}}TapeLabelProvider{{/code}} in the {{code language="none"}}de.cau.cs.rtprak.login.simple.views{{/code}} package that extends [[BaseLabelProvider>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/BaseLabelProvider.html||shape="rect"]] and implements [[ITableLabelProvider>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/ITableLabelProvider.html||shape="rect"]].\\
203 -1*. Add a private field {{code language="none"}}tape{{/code}} of type {{code language="none"}}TuringTape{{/code}} that is initialized from the constructor.
204 -1*. Add fields {{code language="none"}}presentImage{{/code}} and {{code language="none"}}absentImage{{/code}} of type [[Image>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/Image.html||shape="rect"]].
205 -1*. (((
206 -Initialize each image using the following code, where {{code language="none"}}path_to_image{{/code}} is {{code language="none"}}icons/head_present.gif{{/code}} and {{code language="none"}}icons/head_absent.gif{{/code}}, respectively:
207 -
208 -{{code language="java"}}
209 -image = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "path_to_image").createImage();
210 -{{/code}}
211 -)))
212 -1*. Override the implementation of {{code language="none"}}dispose(){{/code}} in {{code language="none"}}TapeLabelProvider{{/code}} to dispose both images after calling {{code language="none"}}super.dispose(){{/code}}. (Right-click in the source-code and click //Source// -> //Override/Implement Methods//.)
213 -1*. In {{code language="none"}}getColumnImage(){{/code}} and {{code language="none"}}getColumnText(){{/code}}, first check whether the element is an instance of {{code language="none"}}TapeData{{/code}} and the column index is 0, and return {{code language="none"}}null{{/code}} otherwise. If the check passes, return the following:\\
214 -1**. {{code language="none"}}getColumnImage(){{/code}}: {{code language="none"}}presentImage{{/code}} if the index given by the tape data element equals the current value of {{code language="none"}}tape.getHeadPosition(){{/code}}, {{code language="none"}}absentImage{{/code}} otherwise.
215 -1**. {{code language="none"}}getColumnText(){{/code}}: a {{code language="none"}}String{{/code}} containing the character of the tape data element.
216 -1. (((
217 -Add the following lines to {{code language="none"}}createPartControl(){{/code}} in {{code language="none"}}TapeViewPart{{/code}}:
218 -
219 -{{code}}
220 -tableViewer.setContentProvider(new TapeContentProvider());
221 -tableViewer.setLabelProvider(new TapeLabelProvider(tape));
222 -tableViewer.setInput(tape);
223 -{{/code}}
224 -)))
225 -
226 -== Use Simple Text Editor as Tape View Input ==
227 -
228 -We will now add code to make the Tape view display the content of a currently active Simple Text Editor.
229 -
230 -1. (((
231 -Add the following methods to {{code language="none"}}SimpleEditorPart{{/code}}:
232 -
233 -{{code language="java"}}
234 -/**
235 - * Returns the text that is currently displayed in the editor.
236 - * @return the currently displayed text
237 - */
238 -public String getText() {
239 - return getDocumentProvider().getDocument(getEditorInput()).get();
240 -}
241 -/** The listener that is currently registered for this editor. */
242 -private IDocumentListener registeredListener;
243 -/**
244 - * Registers the given runnable as listener for changes to the text
245 - * of this editor.
246 - * @param runnable a runnable to register as text listener
247 - */
248 -public void registerTextListener(final Runnable runnable) {
249 - registeredListener = new IDocumentListener() {
250 - public void documentAboutToBeChanged(DocumentEvent event) {}
251 - public void documentChanged(DocumentEvent event) {
252 - runnable.run();
253 - }
254 - };
255 - getDocumentProvider().getDocument(getEditorInput())
256 - .addDocumentListener(registeredListener);
257 -}
258 -/**
259 - * Removes the last registered text listener.
260 - */
261 -public void disposeTextListener() {
262 - if (registeredListener != null) {
263 - if (getDocumentProvider() != null) {
264 - getDocumentProvider().getDocument(getEditorInput())
265 - .removeDocumentListener(registeredListener);
266 - }
267 - registeredListener = null;
268 - }
269 -}
270 -{{/code}}
271 -)))
272 -1. (((
273 -Add the following code to {{code language="none"}}TapeViewPart{{/code}}:
274 -
275 -{{code language="java"}}
276 -/** The editor part that is currently set as input for the viewer. */
277 -private SimpleEditorPart currentInput;
278 -/**
279 - * Sets the displayed text of the given editor part as input of the
280 - * viewer, if the editor part is a SimpleEditorPart.
281 - * @param part workbench part to set as input
282 - */
283 -private void setInput(final IWorkbenchPart part) {
284 - if (part instanceof SimpleEditorPart && part != currentInput) {
285 - if (currentInput != null) {
286 - currentInput.disposeTextListener();
287 - }
288 - currentInput = (SimpleEditorPart) part;
289 - Runnable runnable = new Runnable() {
290 - public void run() {
291 - tape.setText(new StringBuffer(currentInput.getText()));
292 - tableViewer.refresh();
293 - }
294 - };
295 - runnable.run();
296 - currentInput.registerTextListener(runnable);
297 - }
298 -}
299 -{{/code}}
300 -)))
301 -1. (((
302 -Add the following code to {{code language="none"}}createPartControl(){{/code}}:
303 -
304 -{{code language="java"}}
305 -IWorkbenchWindow workbenchWindow = getSite().getWorkbenchWindow();
306 -IWorkbenchPage activePage = workbenchWindow.getActivePage();
307 -if (activePage != null) {
308 - setInput(activePage.getActivePart());
309 -}
310 -workbenchWindow.getPartService().addPartListener(new IPartListener() {
311 - public void partActivated(final IWorkbenchPart part) {
312 - setInput(part);
313 - }
314 - public void partDeactivated(final IWorkbenchPart part) {}
315 - public void partBroughtToTop(final IWorkbenchPart part) {}
316 - public void partClosed(final IWorkbenchPart part) {}
317 - public void partOpened(final IWorkbenchPart part) {}
318 -});
319 -{{/code}}
320 -)))
321 -
322 -== Create Actions to Move the Tape Head ==
323 -
324 -If we want to add buttons to the view's tool bar, we will have to ask its [[IToolbarManager>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/action/IToolBarManager.html||shape="rect"]] to do that for us:
325 -
326 -1. (((
327 -Get the tool bar manager using the following code:
328 -
329 -{{code language="java"}}
330 -IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
331 -{{/code}}
332 -)))
333 -1. Add two actions to the toolbar manager by extending the class [[Action>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/action/Action.html||shape="rect"]] and implementing the {{code language="none"}}run(){{/code}}method.
334 -1*. It is convenient to add actions as anonymous nested classes.
335 -1*. The first action shall have the text "L". When it is run, it shall move the head to the left (to the top in the table viewer), if the head is not already at position 0.
336 -1*. The second action shall have the text "R". When it is run, it shall move the head to the right.
337 -1*. You should call {{code language="none"}}tableViewer.refresh(){{/code}} after any change to the {{code language="none"}}tape.headPosition{{/code}} variable.
338 -
339 -== Test the View ==
340 -
341 -If you open an instance of the simple text editor and open the Tape view, the view should correctly display the editor's text on a tape, and the L and R buttons should move the tape head.
342 -
343 343  = Creating an Extension Point =
344 344  
345 -For the final part of the tutorial, we will now use the extension point mechanism of Eclipse to add some behavior to our Turing Machines. An //extension point// is basically a well-defined point where other plug-ins can register to add functionality. The extension point is basically defined by an XML Schema file that defines an interface; other plug-ins may access this interface using XML code in their {{code language="none"}}plugin.xml{{/code}} file, so-called //extensions//. Our extension point will provide an interface for classes that define behavior of a Turing Machine, and we will call them head controllers (programs that control the tape head).
165 +WRITE THIS SECTION
346 346  
347 -== Defining a Command Class ==
167 +
348 348  
349 -We will start by defining a class representing a command that will be passed to a selected head controller.
169 +
350 350  
351 -1. Add a class {{code language="none"}}HeadCommand{{/code}} to the package {{code language="none"}}de.cau.cs.rtprak.login.simple.controller{{/code}}.
352 -1. Add a nested public static enumeration {{code language="none"}}Action{{/code}} with values {{code language="none"}}WRITE{{/code}}, {{code language="none"}}ERASE{{/code}}, and {{code language="none"}}NULL{{/code}}.
353 -1. Add a nested public static enumeration {{code language="none"}}Direction{{/code}} with values {{code language="none"}}LEFT{{/code}}, {{code language="none"}}RIGHT{{/code}}, and {{code language="none"}}NONE{{/code}}.
354 -1. (((
355 -Add the following private fields:
171 +
356 356  
357 -{{code language="java"}}
358 -private Action action;
359 -private Direction direction;
360 -private char newChar;
361 -{{/code}}
362 -)))
363 -1. Add a constructor to initialize the fields.
364 -1. Add getter methods to access the fields.
365 -
366 -== Defining the Controller Interface ==
367 -
368 -We will now define an interface that all head controllers will have to implement:
369 -
370 -1. Add an interface {{code language="none"}}IHeadController{{/code}} in the package {{code language="none"}}de.cau.cs.rtprak.login.simple.controller{{/code}}.
371 -1. (((
372 -Add the following methods to the interface:
373 -
374 -{{code language="java"}}
375 -/**
376 - * Calculate the next command depending on the currently seen character.
377 - * @param character the currently seen character
378 - * @return the next command specifying which character to write and
379 - * which direction to move the head
380 - */
381 -HeadCommand nextCommand(char character);
382 -
383 -/**
384 - * Reset the internal state of the head controller.
385 - */
386 -void reset();
387 -{{/code}}
388 -)))
389 -
390 -== Defining the Extension Point ==
391 -
392 -We will now define the extension point that head controllers will be registered at.
393 -
394 -1. Open the {{code language="none"}}plugin.xml{{/code}} file in the //Plugin Manifest Editor// and switch to the //Extension Points// tab.
395 -1. Click the //Add// button and enter {{code language="none"}}de.cau.cs.rtprak.login.simple.headControllers{{/code}} as the extension point's ID, and {{code language="none"}}Head Controllers{{/code}} as its name. Shorten the schema file's file name to {{code language="none"}}schema/headControllers.exsd{{/code}}. Make sure that //Edit extension point schema when done// is checked and click //Finish//.
396 -1. Eclipse will now have opened the new schema file in the //Extension Point Schema Editor//, a graphical editor similar to the //Plugin Manifest Editor// that provides a way to define things that might be easier than directly editing the text files.
397 -1. In the new editor, open the //Definition// tab.
398 -1. Add a new element named {{code language="none"}}controller{{/code}}.
399 -1. Add three new attributes to the {{code language="none"}}controller{{/code}} element:\\
400 -1*. First attribute: name {{code language="none"}}id{{/code}}, use {{code language="none"}}required{{/code}}, type {{code language="none"}}string{{/code}}, translatable {{code language="none"}}false{{/code}}.
401 -1*. Second attribute: name {{code language="none"}}name{{/code}}, use {{code language="none"}}required{{/code}}, type {{code language="none"}}string{{/code}}, translatable {{code language="none"}}true{{/code}}.
402 -1*. Third attribute: name {{code language="none"}}class{{/code}}, use {{code language="none"}}required{{/code}}, type {{code language="none"}}java{{/code}}, implements {{code language="none"}}de.cau.cs.rtprak.login.simple.controller.IHeadController{{/code}}. This is the attribute that will tell us which Java class actually implements the controller that is to be registered at our extension point. To make sure that we know how to speak to the class, we require it to implement the interface we defined for head controllers.
403 -1. Add a sequence to the {{code language="none"}}extension{{/code}} element. Right-click the sequence and click //New// -> //controller//. Set the //Min Occurrences// of the sequence to 0, and set //Max Occurrences// to be //Unbounded//.
404 -1. Save the editor and switch back to the //Plugin Manifest Editor//.
405 -1. On the Runtime tab, add {{code language="none"}}de.cau.cs.rtprak.login.simple.controller{{/code}} to the list of packages exported by the plug-in. This is necessary because plug-ins that want to provide extensions for the extension point must provide a class that implements {{code language="none"}}IHeadController{{/code}}. For this to work, those plug-ins must have access to that interface; thus, we have to export the package containing it.
406 -
407 -== Accessing the Extension Point ==
408 -
409 -We will now add a class that will be in charge of loading all extensions registered at our new extension point.
410 -
411 -1. (((
412 -Add a class {{code language="none"}}HeadControllers{{/code}} to the package {{code language="none"}}de.cau.cs.rtprak.login.simple.controller{{/code}}. Add the following code, replacing {{code language="none"}}login{{/code}} with your login name in {{code language="none"}}EXTENSION_POINT_ID{{/code}} as usual:
413 -
414 -{{code language="java"}}
415 -/**
416 - * Class that gathers extension data from the 'headControllers' extension point
417 - * and publishes this data using the singleton pattern.
418 - * @author msp
419 - */
420 -public class HeadControllers {
421 - /** Identifier of the extension point */
422 - public final static String EXTENSION_POINT_ID = "de.cau.cs.rtprak.login.simple.headControllers";
423 - /** The singleton instance of the {@code HeadControllers} class */
424 - public final static HeadControllers INSTANCE = new HeadControllers();
425 - /** list of head controller ids with associated names. */
426 - private List<String[]> controllerNames = new LinkedList<String[]>();
427 - /** map of controller ids to their runtime instances. */
428 - private Map<String, IHeadController> controllerMap = new HashMap<String, IHeadController>();
429 - /**
430 - * Creates an instance of this class and gathers extension data.
431 - */
432 - HeadControllers() {
433 - IConfigurationElement[] elements = Platform.getExtensionRegistry()
434 - .getConfigurationElementsFor(EXTENSION_POINT_ID);
435 - for (IConfigurationElement element : elements) {
436 - if ("controller".equals(element.getName())) {
437 - String id = element.getAttribute("id");
438 - String name = element.getAttribute("name");
439 - if (id != null && name != null) {
440 - try {
441 - IHeadController controller = (IHeadController)element
442 - .createExecutableExtension("class");
443 - controllerNames.add(new String[] {id, name});
444 - controllerMap.put(id, controller);
445 - }
446 - catch (CoreException exception) {
447 - StatusManager.getManager().handle(exception, Activator.PLUGIN_ID);
448 - }
449 - }
450 - }
451 - }
452 - }
453 -
454 - /**
455 - * Returns a list of controller ids and names. The arrays in the list are
456 - * all of size 2: the first element is an id, and the second element is the
457 - * associated name. The controller name is a user-friendly string to be
458 - * displayed in the UI.
459 - * @return a list of controller ids and names
460 - */
461 - public List<String[]> getControllerNames() {
462 - return controllerNames;
463 - }
464 -
465 - /**
466 - * Returns the head controller instance for the given id.
467 - * @param id identifier of a head controller
468 - * @return the associated controller
469 - */
470 - public IHeadController getController(final String id) {
471 - return controllerMap.get(id);
472 - }
473 -}
474 -{{/code}}
475 -)))
476 -
477 -== Adding Support for Head Controllers to the View ==
478 -
479 -We will now have to add support for head controllers to our view.
480 -
481 -1. Open the {{code language="none"}}TapeViewPart{{/code}} class and add the private fields {{code language="none"}}checkedControllerAction{{/code}} of type [[IAction>>url:http://help.eclipse.org/juno/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/action/IAction.html||shape="rect"]] and {{code language="none"}}currentController{{/code}} of type {{code language="none"}}IHeadController{{/code}}.
482 -1. (((
483 -Add a list of registered head controllers to the view's menu (which can be opened using the small white triangle) in the {{code language="none"}}createPartControl(){{/code}} method:
484 -
485 -{{code language="java"}}
486 -IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
487 -for (String[] controllerName : HeadControllers.INSTANCE.getControllerNames()) {
488 - final String id = controllerName[0];
489 - String name = controllerName[1];
490 - Action action = new Action(name, IAction.AS_RADIO_BUTTON) {
491 - public void run() {
492 - if (checkedControllerAction != null) {
493 - checkedControllerAction.setChecked(false);
494 - }
495 - this.setChecked(true);
496 - checkedControllerAction = this;
497 - currentController = HeadControllers.INSTANCE.getController(id);
498 - }
499 - };
500 - if (checkedControllerAction == null) {
501 - action.run();
502 - }
503 - menuManager.add(action);
504 -}
505 -{{/code}}
506 -)))
507 -1. (((
508 -Implement the following method in the {{code language="none"}}TuringTape{{/code}} class:
509 -
510 -{{code language="java"}}
511 -public void execute(final IHeadController controller)
512 -{{/code}}
513 -
514 -The method shall have the following properties:
515 -
516 -\\
517 -
518 -* Determine the character at the current head position using
519 -
520 -{{code language="none"}}
521 -getCharacter(getHeadPosition())
522 -{{/code}}.
523 -* Call
524 -
525 -{{code language="none"}}
526 -controller.nextCommand()
527 -{{/code}} with the current character as parameter.
528 -* Depending on the action in the returned head command, either write the returned new character to the current position in text (
529 -
530 -{{code language="none"}}
531 -WRITE
532 -{{/code}}), or write the blank symbol (
533 -
534 -{{code language="none"}}
535 -ERASE
536 -{{/code}}), or do nothing. If the current position exceeds the end of the text, append enough blank characters up to the current position, then append the new character.
537 -* Depending on the direction in the returned head command, either move the head to the left (but no further than position 0), or to the right, or do nothing.
538 -)))
539 -1. Copy the files [[attach:step.gif]]and [[attach:reset.gif]]to the icons folder.
540 -1. Add an action to the toolbar of the Tape view with text {{code language="none"}}Step{{/code}} and icon {{code language="none"}}step.png{{/code}} which does the following:\\
541 -1*. Check whether the current head controller is not {{code language="none"}}null{{/code}}, than call {{code language="none"}}tape.execute(currentController){{/code}}.
542 -1*. Refresh the table viewer with its {{code language="none"}}refresh(){{/code}} method.
543 -1*. (((
544 -Note: actions don't need images, but only image descriptors. Thus, to set the action's icon to {{code language="none"}}step.png{{/code}}, you can use something like the following:
545 -
546 -{{code language="java"}}
547 -Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "path_to_icon");
548 -{{/code}}
549 -)))
550 -1. Add another action with text Reset and icon reset.png which does the following:\\
551 -1*. Check whether the current head controller is not {{code language="none"}}null{{/code}}, then call the {{code language="none"}}reset(){{/code}} method on {{code language="none"}}currentController{{/code}}.
552 -1*. Set the current head position to 1.
553 -1*. Refresh the table viewer with its {{code language="none"}}refresh(){{/code}} method.
554 -
555 -== Adding a Test Head Controller ==
556 -
557 -Before creating a proper head controller in another plug-in, we will add a test controller to check whether all this stuff works.
558 -
559 -1. (((
560 -Add a new class {{code language="none"}}NullController{{/code}} to the {{code language="none"}}de.cau.cs.rtprak.login.simple.controllers{{/code}} package:
561 -
562 -{{code language="java"}}
563 -/**
564 - * Head controller that does nothing, for testing.
565 - * @author msp
566 - */
567 -public class NullController implements IHeadController {
568 - /**
569 - * {@inheritDoc}
570 - */
571 - public HeadCommand nextCommand(final char character) {
572 - return new HeadCommand(Action.NULL, Direction.NONE, '_');
573 - }
574 -
575 - /**
576 - * {@inheritDoc}
577 - */
578 - public void reset() {
579 - }
580 -}
581 -{{/code}}
582 -)))
583 -1. Open the //Plugin Manifest Editor// and switch to the //Extensions// tab. Add your {{code language="none"}}de.cau.cs.rtprak.login.simple.headControllers{{/code}} extension point. Add a {{code language="none"}}controller{{/code}} element with ID {{code language="none"}}de.cau.cs.rtprak.login.simple.nullController{{/code}}, name {{code language="none"}}Null Controller{{/code}}, and class {{code language="none"}}de.cau.cs.rtprak.login.simple.controller.NullController{{/code}}.
584 -1. Start the application and observe how your program behaves if you change the action and direction in the {{code language="none"}}NullController{{/code}} class. You can actually change both while the application is running, but only if you have started it in the Debug mode. In that case, Eclipse will actually hot-swap your changes into the running application. Sorcery!
585 -
586 -== Implementing Your Own Head Controller ==
587 -
588 -We will now create a new plug-in with a new head controller:
589 -
590 -1. Create a new plug-in {{code language="none"}}de.cau.cs.rtprak.login.simple.extension{{/code}}. (Remember to create the project in your Git repository.) In the //Plugin Manifest Editor//, add {{code language="none"}}de.cau.cs.rtprak.login.simple{{/code}} to the dependencies of the new plug-in.
591 -1. Create a new class that implements {{code language="none"}}IHeadController{{/code}}:\\
592 -1*. Assuming that the initial head position is 1, the controller shall copy the input text infinitely often. So if the tape initially contains the word {{code language="none"}}hello{{/code}}, the controller shall generate {{code language="none"}}hellohellohellohe...{{/code}} .
593 -1*. Your class needs some private fields to store the internal state of the controller, and you may need some special character as marker. Imagine how a Turing Machine would do this.
594 -1*. It is not allowed to store data that can grow infinitely, since a Turing Machine may only have a finite number of states. This means that you may store single characters or numbers, but you must not store Strings, StringBuffers, arrays, lists, or sets.
595 -1. Register the new controller class using an extension in the new plug-in.
596 -1. Test your controller.
597 -
598 -= Congratulations! =
599 -
600 -Congratulations, you just made a big step towards understanding how Eclipse works. Plus, you've refreshed your knowledge on Turing Machines along the way. Eclipse is an industry standard technology, and having experience programming against it is a valuable skill for you.
601 -
602 -If you have any comments and suggestions for improvement concerning this tutorial, please don't hesitate to tell us about them!
173 +
head_absent.gif
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -94 bytes
Content
head_present.gif
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -370 bytes
Content
presentation.pdf
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -1.3 MB
Content
reset.gif
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -354 bytes
Content
result.png
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -40.5 KB
Content
step.gif
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -216 bytes
Content
tape_head.gif
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -362 bytes
Content
turing-file.gif
Author
... ... @@ -1,1 +1,0 @@
1 -XWiki.XWikiGuest
Size
... ... @@ -1,1 +1,0 @@
1 -593 bytes
Content
Confluence.Code.ConfluencePageClass[0]
Id
... ... @@ -1,1 +1,1 @@
1 -2982234
1 +2982287
URL
... ... @@ -1,1 +1,1 @@
1 -https://rtsys.informatik.uni-kiel.de/confluence//wiki/spaces/WS12EclPract/pages/2982234/The Plug-in Architecture of Eclipse
1 +https://rtsys.informatik.uni-kiel.de/confluence//wiki/spaces/WS12EclPract/pages/2982287/The Plug-in Architecture of Eclipse