001 // This file is part of the Attempto Java Packages.
002 // Copyright 2008, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
003 //
004 // The Attempto Java Packages is free software: you can redistribute it and/or modify it under the
005 // terms of the GNU Lesser General Public License as published by the Free Software Foundation,
006 // either version 3 of the License, or (at your option) any later version.
007 //
008 // The Attempto Java Packages is distributed in the hope that it will be useful, but WITHOUT ANY
009 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
010 // PURPOSE. See the GNU Lesser General Public License for more details.
011 //
012 // You should have received a copy of the GNU Lesser General Public License along with the Attempto
013 // Java Packages. If not, see http://www.gnu.org/licenses/.
014
015 package ch.uzh.ifi.attempto.acewiki;
016
017 import java.io.IOException;
018 import java.io.InputStream;
019 import java.io.OutputStream;
020 import java.util.ArrayList;
021 import java.util.Collection;
022 import java.util.Properties;
023 import java.util.Random;
024 import java.util.Stack;
025
026 import nextapp.echo2.app.Alignment;
027 import nextapp.echo2.app.ApplicationInstance;
028 import nextapp.echo2.app.Color;
029 import nextapp.echo2.app.Column;
030 import nextapp.echo2.app.ContentPane;
031 import nextapp.echo2.app.Extent;
032 import nextapp.echo2.app.Font;
033 import nextapp.echo2.app.Insets;
034 import nextapp.echo2.app.ResourceImageReference;
035 import nextapp.echo2.app.Row;
036 import nextapp.echo2.app.SplitPane;
037 import nextapp.echo2.app.TaskQueueHandle;
038 import nextapp.echo2.app.event.ActionEvent;
039 import nextapp.echo2.app.event.ActionListener;
040 import nextapp.echo2.app.filetransfer.Download;
041 import nextapp.echo2.app.filetransfer.DownloadProvider;
042 import ch.uzh.ifi.attempto.acewiki.core.grammar.StandardGrammar;
043 import ch.uzh.ifi.attempto.acewiki.core.ontology.Individual;
044 import ch.uzh.ifi.attempto.acewiki.core.ontology.NounConcept;
045 import ch.uzh.ifi.attempto.acewiki.core.ontology.OfRole;
046 import ch.uzh.ifi.attempto.acewiki.core.ontology.Ontology;
047 import ch.uzh.ifi.attempto.acewiki.core.ontology.OntologyElement;
048 import ch.uzh.ifi.attempto.acewiki.core.ontology.TrAdjRole;
049 import ch.uzh.ifi.attempto.acewiki.core.ontology.VerbRole;
050 import ch.uzh.ifi.attempto.acewiki.core.text.OntologyTextElement;
051 import ch.uzh.ifi.attempto.acewiki.gui.ListItem;
052 import ch.uzh.ifi.attempto.acewiki.gui.editor.NounForm;
053 import ch.uzh.ifi.attempto.acewiki.gui.editor.NounOfForm;
054 import ch.uzh.ifi.attempto.acewiki.gui.editor.ProperNameForm;
055 import ch.uzh.ifi.attempto.acewiki.gui.editor.TrAdjForm;
056 import ch.uzh.ifi.attempto.acewiki.gui.editor.VerbForm;
057 import ch.uzh.ifi.attempto.acewiki.gui.editor.WordEditorWindow;
058 import ch.uzh.ifi.attempto.acewiki.gui.page.ArticlePage;
059 import ch.uzh.ifi.attempto.acewiki.gui.page.IndexPage;
060 import ch.uzh.ifi.attempto.acewiki.gui.page.SearchPage;
061 import ch.uzh.ifi.attempto.acewiki.gui.page.StartPage;
062 import ch.uzh.ifi.attempto.acewiki.gui.page.WikiPage;
063 import ch.uzh.ifi.attempto.chartparser.Grammar;
064 import ch.uzh.ifi.attempto.echocomp.GeneralButton;
065 import ch.uzh.ifi.attempto.echocomp.Label;
066 import ch.uzh.ifi.attempto.echocomp.MessageWindow;
067 import ch.uzh.ifi.attempto.echocomp.SmallButton;
068 import ch.uzh.ifi.attempto.echocomp.SolidLabel;
069 import ch.uzh.ifi.attempto.echocomp.Style;
070 import ch.uzh.ifi.attempto.echocomp.TextField;
071 import ch.uzh.ifi.attempto.echocomp.VSpace;
072 import ch.uzh.ifi.attempto.echocomp.WindowPane;
073 import ch.uzh.ifi.attempto.preditor.PreditorWindow;
074
075 /**
076 * This class represents an AceWiki wiki.
077 *
078 * @author Tobias Kuhn
079 */
080 public class Wiki implements ActionListener {
081
082 private static final long serialVersionUID = 2777443689044226043L;
083
084 private final Ontology ontology;
085
086 private WikiPage currentPage;
087 private ContentPane mainPane = new ContentPane();
088 private ContentPane contentPane = new ContentPane();
089 private Row navigationButtons = new Row();
090 private Logger logger;
091
092 private GeneralButton backButton = new GeneralButton("<Back", this);
093 private GeneralButton forwardButton = new GeneralButton("Forward>", this);
094 private GeneralButton refreshButton = new GeneralButton("Refresh", this);
095
096 private SmallButton indexButton = new SmallButton("Index", this, 12);
097 private SmallButton homeButton = new SmallButton("Main Page", this, 12);
098 private SmallButton randomButton = new SmallButton("Random Article", this, 12);
099 private SmallButton searchButton = new SmallButton("Search:", this, 12);
100 private TextField searchTextField = new TextField(110, this);
101 private SmallButton newButton = new SmallButton("New Word...", this, 12);
102 private SmallButton exportButton = new SmallButton("Export", this, 12);
103 private SmallButton logoutButton = new SmallButton("Logout", this, 12);
104 private ListItem logoutListItem;
105 private Label logo;
106
107 private StartPage startPage;
108
109 private Stack<WikiPage> history = new Stack<WikiPage>();
110 private Stack<WikiPage> forward = new Stack<WikiPage>();
111
112 private ArrayList<WindowPane> windows = new ArrayList<WindowPane>();
113
114 private Grammar grammar = new StandardGrammar();
115
116 private TaskQueueHandle taskQueue;
117
118 private ApplicationInstance application;
119
120 private static Properties properties;
121
122 /**
123 * Creates a new wiki instance.
124 *
125 * @param ontology The ontology that is loaded into the wiki.
126 * @param title The title of the wiki.
127 * @param description The description of the wiki.
128 * @param sessionID The session id.
129 */
130 Wiki(Ontology ontology, String title, String description, int sessionID) {
131 this.ontology = ontology;
132
133 logger = new Logger(ontology.getName(), sessionID);
134 application = ApplicationInstance.getActive();
135 taskQueue = application.createTaskQueue();
136
137 SplitPane splitPane1 = new SplitPane(SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM);
138 splitPane1.setSeparatorPosition(new Extent(50));
139 splitPane1.setSeparatorHeight(new Extent(0));
140
141 navigationButtons.setInsets(new Insets(5, 5, 5, 26));
142 navigationButtons.setCellSpacing(new Extent(5));
143 navigationButtons.setBackground(new Color(230, 230, 230));
144
145 navigationButtons.add(backButton);
146 navigationButtons.add(forwardButton);
147 navigationButtons.add(refreshButton);
148
149 ContentPane menuBar = new ContentPane();
150 menuBar.add(navigationButtons);
151
152 SplitPane splitPane2 = new SplitPane(SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT, new Extent(145));
153 splitPane2.setSeparatorHeight(new Extent(0));
154
155 ContentPane sideBar = new ContentPane();
156 sideBar.setBackground(new Color(230, 230, 230));
157 Column sideCol = new Column();
158 sideCol.setInsets(new Insets(10, 10));
159 sideCol.setCellSpacing(new Extent(1));
160
161 logo = new Label(new ResourceImageReference("ch/uzh/ifi/attempto/acewiki/gui/img/AceWikiLogoSmall.png"));
162 sideCol.add(logo);
163
164 sideCol.add(new VSpace(30));
165
166 SolidLabel label1 = new SolidLabel("Navigation:", Font.ITALIC);
167 label1.setFont(new Font(Style.fontTypeface, Font.ITALIC, new Extent(10)));
168 sideCol.add(label1);
169 sideCol.add(new ListItem(homeButton));
170 sideCol.add(new ListItem(indexButton));
171 sideCol.add(new ListItem(randomButton));
172 sideCol.add(new ListItem(searchButton, null, searchTextField));
173
174 sideCol.add(new VSpace(10));
175
176 SolidLabel label2 = new SolidLabel("Actions:", Font.ITALIC);
177 label2.setFont(new Font(Style.fontTypeface, Font.ITALIC, new Extent(10)));
178 sideCol.add(label2);
179 sideCol.add(new ListItem(newButton));
180 sideCol.add(new ListItem(exportButton));
181 logoutListItem = new ListItem(logoutButton);
182 logoutButton.setWidth(new Extent(110));
183 logoutButton.setAlignment(new Alignment(Alignment.LEFT, Alignment.CENTER));
184 logoutListItem.setVisible(false);
185 sideCol.add(logoutListItem);
186
187 //sideCol.add(new VSpace(20));
188 //sideCol.add(new ItalicLabel("Session ID: " + sessionID));
189
190 sideBar.add(sideCol);
191
192 SplitPane splitPane3 = new SplitPane(SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT);
193 splitPane3.setSeparatorWidth(new Extent(1));
194 splitPane3.setSeparatorColor(Color.BLACK);
195 splitPane3.setSeparatorPosition(new Extent(0));
196 splitPane3.add(new Label());
197
198 SplitPane splitPane4 = new SplitPane(SplitPane.ORIENTATION_VERTICAL_TOP_BOTTOM);
199 splitPane4.setSeparatorHeight(new Extent(1));
200 splitPane4.setSeparatorColor(Color.BLACK);
201 splitPane4.setSeparatorPosition(new Extent(0));
202 splitPane4.add(new Label());
203
204 splitPane3.add(splitPane4);
205 splitPane4.add(mainPane);
206
207 splitPane1.add(menuBar);
208 splitPane1.add(splitPane3);
209
210 splitPane2.add(sideBar);
211 splitPane2.add(splitPane1);
212
213 contentPane.add(splitPane2);
214
215 startPage = new StartPage(this, title, description);
216
217 setCurrentPage(startPage);
218 update();
219 }
220
221 /**
222 * Returns the content pane containing the wiki GUI.
223 *
224 * @return The content pane.
225 */
226 public ContentPane getContentPane() {
227 return contentPane;
228 }
229
230 /**
231 * Displays the window in the wiki.
232 *
233 * @param window The window to be shown.
234 */
235 public void showWindow(final WindowPane window) {
236 if (window instanceof WordEditorWindow || window instanceof PreditorWindow) {
237 ArrayList<WindowPane> windowsNew = new ArrayList<WindowPane>();
238 for (WindowPane wp : windows) {
239 if (wp.isVisible()) windowsNew.add(wp);
240 }
241 windows = windowsNew;
242 int c = windows.size();
243 window.setPositionX(new Extent(50 + (c % 5)*40));
244 window.setPositionY(new Extent(50 + (c % 5)*20));
245 windows.add(window);
246 }
247 getContentPane().add(window);
248 }
249
250 /**
251 * Switches to the given page.
252 *
253 * @param page The page to switch to.
254 */
255 public void showPage(WikiPage page) {
256 if (!currentPage.equals(page)) {
257 history.push(currentPage);
258 forward.clear();
259 }
260 setCurrentPage(page);
261 log("navi", "goto: " + page);
262 update();
263 }
264
265 /**
266 * Switches to the page of the given ontology element.
267 *
268 * @param e The ontology element the page of which should be shown.
269 */
270 public void showPage(OntologyElement e) {
271 showPage(ArticlePage.create(e, this));
272 }
273
274 /**
275 * Go to the previous page in the history.
276 */
277 public void back() {
278 if (history.isEmpty()) return;
279 forward.push(currentPage);
280 WikiPage page = history.pop();
281 setCurrentPage(page);
282 log("navi", "back: " + page);
283 update();
284 }
285
286 /**
287 * Go to the next page in the history.
288 */
289 public void forward() {
290 if (forward.isEmpty()) return;
291 history.push(currentPage);
292 WikiPage page = forward.pop();
293 setCurrentPage(page);
294 log("navi", "forw: " + page);
295 update();
296 }
297
298 /**
299 * Show the start page.
300 */
301 public void showStartPage() {
302 showPage(startPage);
303 }
304
305 /**
306 * Show the index page.
307 */
308 public void showIndexPage() {
309 showPage(new IndexPage(this));
310 }
311
312 /**
313 * Show the search page.
314 */
315 public void showSearchPage() {
316 showPage(new SearchPage(this, ""));
317 }
318
319 /**
320 * Returns the ontology;
321 *
322 * @return The ontology.
323 */
324 public Ontology getOntology() {
325 return ontology;
326 }
327
328 /**
329 * Returns all ontology elements.
330 *
331 * @return A collection of all ontology elements.
332 */
333 public Collection<OntologyElement> getOntologyElements() {
334 return ontology.getOntologyElements();
335 }
336
337 /**
338 * Updates the GUI.
339 */
340 public void update() {
341 mainPane.removeAll();
342 mainPane.add(currentPage);
343
344 backButton.setEnabled(!history.isEmpty());
345 forwardButton.setEnabled(!forward.isEmpty());
346
347 // The commented-out code below checks at every GUI update whether the ontology is consistent or not.
348 // If not, a red AceWiki logo is shown. Usually, this case should never occur because we check for
349 // consistency after every new statement.
350 //if (ontology.isConsistent()) {
351 // logo.setIcon(new ResourceImageReference("ch/uzh/ifi/attempto/acewiki/gui/img/AceWikiLogoSmall.png"));
352 //} else {
353 // logo.setIcon(new ResourceImageReference("ch/uzh/ifi/attempto/acewiki/gui/img/AceWikiLogoSmallRed.png"));
354 //}
355 }
356
357 private void setCurrentPage(WikiPage currentPage) {
358 this.currentPage = currentPage;
359 refresh();
360 }
361
362 /**
363 * Refreshes the current page.
364 */
365 public void refresh() {
366 currentPage.update();
367 }
368
369 public void actionPerformed(ActionEvent e) {
370 if (e.getSource() == backButton) {
371 log("page", "pressed: back");
372 back();
373 } else if (e.getSource() == forwardButton) {
374 log("page", "pressed: forward");
375 forward();
376 } else if (e.getSource() == indexButton) {
377 log("page", "pressed: index");
378 showIndexPage();
379 } else if (e.getSource() == homeButton) {
380 log("page", "pressed: main page");
381 showStartPage();
382 } else if (e.getSource() == randomButton) {
383 log("page", "pressed: random page");
384 ArrayList<OntologyElement> elements = new ArrayList<OntologyElement>(ontology.getOntologyElements());
385 if (elements.size() > 0) {
386 int r = (new Random()).nextInt(elements.size());
387 showPage(elements.get(r));
388 } else {
389 showStartPage();
390 }
391 } else if (e.getSource() == refreshButton) {
392 log("page", "pressed: refresh");
393 update();
394 refresh();
395 } else if (e.getSource() == newButton) {
396 log("page", "pressed: new word");
397 WordEditorWindow w = WordEditorWindow.createCreatorWindow();
398 w.addTab(new ProperNameForm(new Individual(), w, this, this));
399 w.addTab(new NounForm(new NounConcept(), 0, w, this, this));
400 w.addTab(new NounOfForm(new OfRole(), w, this, this));
401 w.addTab(new VerbForm(new VerbRole(), 0, w, this, this));
402 w.addTab(new TrAdjForm(new TrAdjRole(), w, this, this));
403 showWindow(w);
404 } else if (e.getSource() == searchButton || e.getSource() == searchTextField) {
405 log("page", "pressed: search '" + searchTextField.getText() + "'");
406 String s = searchTextField.getText();
407 searchTextField.setText("");
408 OntologyElement el = ontology.get(s.replace(' ', '_'));
409 if (el == null) {
410 showPage(new SearchPage(this, s));
411 } else {
412 showPage(el);
413 }
414 } else if (e.getSource() == exportButton) {
415 final String f = ontology.getOWLOntologyAsXML();
416 DownloadProvider provider = new DownloadProvider() {
417
418 public String getContentType() {
419 return "application/owl+xml";
420 }
421
422 public String getFileName() {
423 return ontology.getName() + ".owl";
424 }
425
426 public int getSize() {
427 return f.length();
428 }
429
430 public void writeFile(OutputStream out) throws IOException {
431 out.write(f.getBytes());
432 out.close();
433 }
434
435 };
436 application.enqueueCommand(new Download(provider, true));
437 } else if (e.getSource() == logoutButton) {
438 showWindow(new MessageWindow("Logout", "Do you really want to log out?", null, this, "Yes", "No"));
439 } else if (e.getSource() instanceof MessageWindow && e.getActionCommand().equals("Yes")) {
440 ((AceWikiApp) ApplicationInstance.getActive()).logout();
441 } else if (e.getSource() instanceof OntologyTextElement) {
442 // for newly generated elements
443 OntologyTextElement te = (OntologyTextElement) e.getSource();
444 log("edit", "new word: " + te.getOntologyElement().getWord());
445 showPage(te.getOntologyElement());
446 }
447 }
448
449 /**
450 * Writes the log entry to the log file.
451 *
452 * @param type The type of the log entry.
453 * @param text The text of the log entry.
454 */
455 public void log(String type, String text) {
456 logger.log(type, text);
457 }
458
459 /**
460 * Sets the user name.
461 *
462 * @param username The user name.
463 */
464 public void setUsername(String username) {
465 logger.setUsername(username);
466 logoutButton.setText("Logout: " + username);
467 logoutListItem.setVisible(true);
468 }
469
470 /**
471 * Returns the grammar to be used for this wiki.
472 *
473 * @return The grammar.
474 */
475 public Grammar getGrammar() {
476 return grammar;
477 }
478
479 /**
480 * Runs the task without showing a wait window while it is executed.
481 *
482 * @param task The task.
483 */
484 public void enqueueTask(Runnable task) {
485 application.enqueueTask(taskQueue, task);
486 }
487
488 /**
489 * Runs the task and shows a wait window while it is executed.
490 *
491 * @param title The title of the wait window.
492 * @param message The message of the wait window.
493 * @param task The task.
494 */
495 public void enqueueTaskShowingWaitWindow(String title, String message, final Task task) {
496 final MessageWindow waitWindow = new MessageWindow(
497 title,
498 new ResourceImageReference("ch/uzh/ifi/attempto/acewiki/gui/img/wait.gif"),
499 message,
500 null,
501 null
502 );
503 waitWindow.setClosable(false);
504 showWindow(waitWindow);
505
506 Thread thread = new Thread() {
507 public void run() {
508 task.run();
509 application.enqueueTask(taskQueue, new Runnable() {
510 public synchronized void run() {
511 waitWindow.setVisible(false);
512 task.updateGUI();
513 }
514 });
515 }
516 };
517 thread.start();
518 }
519
520 public static String getInfo(String key) {
521 if (properties == null) {
522 String f = "ch/uzh/ifi/attempto/acewiki/acewiki.properties";
523 InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(f);
524 properties = new Properties();
525 try {
526 properties.load(in);
527 } catch (Exception ex) {
528 ex.printStackTrace();
529 }
530 }
531
532 return properties.getProperty(key);
533 }
534
535 }