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    }