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