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