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