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    }