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