001    // This file is part of AceWiki.
002    // Copyright 2008-2012, AceWiki developers.
003    // 
004    // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU
005    // Lesser General Public License as published by the Free Software Foundation, either version 3 of
006    // the License, or (at your option) any later version.
007    // 
008    // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009    // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
010    // Lesser General Public License for more details.
011    // 
012    // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If
013    // not, see http://www.gnu.org/licenses/.
014    
015    package ch.uzh.ifi.attempto.aceeditor;
016    
017    import java.io.IOException;
018    import java.io.InputStream;
019    import java.io.OutputStream;
020    import java.util.List;
021    import java.util.Map;
022    import java.util.Properties;
023    
024    import nextapp.echo.app.ApplicationInstance;
025    import nextapp.echo.app.Column;
026    import nextapp.echo.app.Component;
027    import nextapp.echo.app.Extent;
028    import nextapp.echo.app.Insets;
029    import nextapp.echo.app.SplitPane;
030    import nextapp.echo.app.Window;
031    import nextapp.echo.app.WindowPane;
032    import nextapp.echo.app.event.ActionEvent;
033    import nextapp.echo.app.event.ActionListener;
034    import nextapp.echo.filetransfer.app.AbstractDownloadProvider;
035    import nextapp.echo.filetransfer.app.DownloadCommand;
036    import nextapp.echo.webcontainer.command.BrowserRedirectCommand;
037    import ch.uzh.ifi.attempto.base.TextContainer;
038    import ch.uzh.ifi.attempto.base.TextElement;
039    import ch.uzh.ifi.attempto.chartparser.ChartParser;
040    import ch.uzh.ifi.attempto.echocomp.MessageWindow;
041    import ch.uzh.ifi.attempto.echocomp.TextAreaWindow;
042    import ch.uzh.ifi.attempto.echocomp.UploadWindow;
043    import ch.uzh.ifi.attempto.preditor.PreditorWindow;
044    
045    /**
046     * This is the main class of the ACE Editor web application. The ACE Editor allows users to write
047     * sentences in ACE by the use of a predictive editor. Users can extend the lexicon and they can
048     * upload their own lexica.
049     * 
050     * @author Tobias Kuhn
051     */
052    public class ACEEditor extends Window implements ActionListener {
053    
054            private static final long serialVersionUID = -684743065195237612L;
055            
056            private static Properties properties;
057    
058            private boolean editMode;
059            private LexiconHandler lexiconHandler;
060            private Map<String, String> parameters;
061    
062            private TextEntry selectedEntry;
063            private TextEntry finalEntry = new TextEntry(null, this);
064            private TextEntry clipboard;
065            
066            private Column textColumn = new Column();
067            private Column mainColumn = new Column();
068            private MenuBar menuBar;
069            
070            // TODO: reactive key combinations
071    //      private KeyStrokeListener keyStrokeListener = new KeyStrokeListener();
072            
073            /**
074             * Creates a new ACE Editor application.
075             * 
076             * @param parameters A set of parameters in the form of name/value pairs.
077             */
078            public ACEEditor(Map<String, String> parameters) {
079                    setTitle("ACE Editor");
080                    this.parameters = parameters;
081                    
082                    lexiconHandler = new LexiconHandler(parameters.get("lexicon"));
083    
084                    SplitPane splitPane = new SplitPane(SplitPane.ORIENTATION_VERTICAL);
085                    splitPane.setSeparatorPosition(new Extent(23));
086    
087                    menuBar = new MenuBar(this);
088                    menuBar.setSelected("Default Expanded", true);
089                    menuBar.setSelected("Default Paraphrase", true);
090                    menuBar.setSelected("Default Syntax Boxes", true);
091                    menuBar.setSelected("Default Pretty-Printed DRS", true);
092                    menuBar.setEnabled("Paste", false);
093                    splitPane.add(menuBar.getContent());
094    
095                    textColumn.setInsets(new Insets(0, 5));
096                    textColumn.add(finalEntry);
097    
098                    mainColumn.add(textColumn);
099                    
100    //              // Up and down keys for moving the selection:
101    //              keyStrokeListener.addKeyCombination(VK_UP, "Up Pressed");
102    //              keyStrokeListener.addKeyCombination(VK_DOWN, "Down Pressed");
103    //
104    //              // Space key for expand/collapse or add:
105    //              keyStrokeListener.addKeyCombination(VK_SPACE, "Space Pressed");
106    //
107    //              // Backspace key for delete:
108    //              keyStrokeListener.addKeyCombination(VK_BACK_SPACE, "Backspace Pressed");
109    //
110    //              // Function key + A for add:
111    //              keyStrokeListener.addKeyCombination(VK_A | CONTROL_MASK, "Func-A Pressed");
112    //              keyStrokeListener.addKeyCombination(VK_A | META_MASK, "Func-A Pressed");
113    //              keyStrokeListener.addKeyCombination(VK_A | ALT_MASK, "Func-A Pressed");
114    //
115    //              // Function key + M for modify:
116    //              keyStrokeListener.addKeyCombination(VK_M | CONTROL_MASK, "Func-M Pressed");
117    //              keyStrokeListener.addKeyCombination(VK_M | META_MASK, "Func-M Pressed");
118    //              keyStrokeListener.addKeyCombination(VK_M | ALT_MASK, "Func-M Pressed");
119    //
120    //              // Function key + X for cut:
121    //              keyStrokeListener.addKeyCombination(VK_X | CONTROL_MASK, "Func-X Pressed");
122    //              keyStrokeListener.addKeyCombination(VK_X | META_MASK, "Func-X Pressed");
123    //              keyStrokeListener.addKeyCombination(VK_X | ALT_MASK, "Func-X Pressed");
124    //
125    //              // Function key + C for copy:
126    //              keyStrokeListener.addKeyCombination(VK_C | CONTROL_MASK, "Func-C Pressed");
127    //              keyStrokeListener.addKeyCombination(VK_C | META_MASK, "Func-C Pressed");
128    //              keyStrokeListener.addKeyCombination(VK_C | ALT_MASK, "Func-C Pressed");
129    //
130    //              // Function key + V for paste:
131    //              keyStrokeListener.addKeyCombination(VK_V | CONTROL_MASK, "Func-V Pressed");
132    //              keyStrokeListener.addKeyCombination(VK_V | META_MASK, "Func-V Pressed");
133    //              keyStrokeListener.addKeyCombination(VK_V | ALT_MASK, "Func-V Pressed");
134    //
135    //              // Function key + O for open:
136    //              keyStrokeListener.addKeyCombination(VK_O | CONTROL_MASK, "Func-O Pressed");
137    //              keyStrokeListener.addKeyCombination(VK_O | META_MASK, "Func-O Pressed");
138    //              keyStrokeListener.addKeyCombination(VK_O | ALT_MASK, "Func-O Pressed");
139    //
140    //              // Function key + S for save:
141    //              keyStrokeListener.addKeyCombination(VK_S | CONTROL_MASK, "Func-S Pressed");
142    //              keyStrokeListener.addKeyCombination(VK_S | META_MASK, "Func-S Pressed");
143    //              keyStrokeListener.addKeyCombination(VK_S | ALT_MASK, "Func-S Pressed");
144    //
145    //              keyStrokeListener.addActionListener(this);
146    //              mainColumn.add(keyStrokeListener);
147    
148                    splitPane.add(mainColumn);
149                    getContent().add(splitPane);
150                    
151                    select(finalEntry);
152            }
153    
154            /**
155             * Returns whether parsing with the compiled lexicon of the APE executable is enabled.
156             * 
157             * @return true if parsing with the compiled lexicon is enabled.
158             */
159            public boolean isParseWithClexEnabled() {
160                    return !"off".equals(getParameter("parse_with_clex"));
161            }
162    
163            /**
164             * Returns whether the lexicon is immutable or can be changed by users.
165             * 
166             * @return true if the lexicon is immutable.
167             */
168            public boolean isLexiconImmutable() {
169                    return !"off".equals(getParameter("immutable_lexicon"));
170            }
171            
172            /**
173             * Returns the maximum file size (in bytes) for file upload. 0 means unlimited file size.
174             * 
175             * @return The maximum file size.
176             */
177            public int getMaxUploadFileSize() {
178                    try {
179                            return Integer.parseInt(getParameter("max_upload_file_size"));
180                    } catch (NumberFormatException ex) {}
181                    return 0;
182            }
183            
184            /**
185             * Returns the value of the given parameter. These parameters are defined in the web.xml file
186             * of the web application.
187             * 
188             * @param paramName The parameter name.
189             * @return The value of the parameter.
190             */
191            public String getParameter(String paramName) {
192                    return parameters.get(paramName);
193            }
194    
195            /**
196             * Returns the full text of the current content of this ACE Editor instance.
197             * 
198             * @return The full text.
199             */
200            public String getFullText() {
201                    String text = "";
202                    for (Component c : textColumn.getComponents()) {
203                            String s = ((TextEntry) c).getText();
204                            if (c == finalEntry) break;
205                            if (s == null) s = "";
206                            text += s + "\n\n";
207                    }
208                    return text;
209            }
210    
211            LexiconHandler getLexiconHandler() {
212                    return lexiconHandler;
213            }
214    
215            void select(TextEntry entry) {
216                    if (selectedEntry != null) {
217                            selectedEntry.setSelected(false);
218                    }
219                    entry.setSelected(true);
220                    selectedEntry = entry;
221    
222                    if (selectedEntry == finalEntry) {
223                            menuBar.setEnabled("Delete", false);
224                            menuBar.setEnabled("Cut", false);
225                    } else {
226                            menuBar.setEnabled("Delete", true);
227                            menuBar.setEnabled("Cut", true);
228                    }
229                    if (selectedEntry.isEmpty()) {
230                            menuBar.setEnabled("Modify...", false);
231                    } else {
232                            menuBar.setEnabled("Modify...", true);
233                    }
234                    if (selectedEntry.isEmpty() || selectedEntry.isComment()) {
235                            menuBar.setEnabled("Expanded", false);
236                            menuBar.setSelected("Expanded", false);
237                            for (String s : ResultItem.TYPES) {
238                                    menuBar.setEnabled("Show " + s, false);
239                                    menuBar.setSelected("Show " + s, false);
240                            }
241                    } else {
242                            menuBar.setEnabled("Expanded", true);
243                            menuBar.setSelected("Expanded", selectedEntry.isExpanded());
244                            for (String s : ResultItem.TYPES) {
245                                    menuBar.setEnabled("Show " + s, true);
246                                    menuBar.setSelected("Show " + s, selectedEntry.isResultItemVisible(s));
247                            }
248                    }
249                    menuBar.update();
250            }
251    
252            void entryChanged(TextEntry entry) {
253                    if (entry == selectedEntry) {
254                            menuBar.setSelected("Expanded", selectedEntry.isExpanded());
255                            menuBar.update();
256                    }
257            }
258            
259            void showWindow(WindowPane window) {
260                    cleanWindows();
261                    getContent().add(window);
262            }
263            
264            void removeWindow(WindowPane window) {
265                    window.setVisible(false);
266                    window.dispose();
267                    cleanWindows();
268            }
269            
270            private void cleanWindows() {
271                    for (Component c : getContent().getComponents()) {
272                            if (!c.isVisible()) {
273                                    getContent().remove(c);
274                            }
275                    }
276            }
277            
278            public void actionPerformed(ActionEvent e) {
279                    String c = e.getActionCommand();
280                    Object source = e.getSource();
281    
282                    if (c.equals("About")) {
283                            String v = getInfo("aceeditor-version");
284                            String r = getInfo("aceeditor-release-stage");
285                            String d = getInfo("aceeditor-build-date");
286                            showWindow(new MessageWindow(
287                                            "ACE Editor",
288                                            "ACE Editor " + v + " (" + r + "), " + d,
289                                            "OK"
290                                    ));
291                    } else if (c.equals("Attempto Website")) {
292                            ApplicationInstance.getActive().enqueueCommand(
293                                            new BrowserRedirectCommand("http://attempto.ifi.uzh.ch")
294                                    );
295                    } else if (c.equals("Open Text...")) {
296                            openFile();
297                    } else if (c.equals("Save Text...")) {
298                            saveFile();
299                    } else if (c.equals("Load Lexicon...")) {
300                            loadLexicon(false);
301                    } else if (c.equals("Replace Lexicon...")) {
302                            loadLexicon(true);
303                    } else if (c.equals("Save Lexicon...")) {
304                            saveLexicon();
305                    } else if (c.equals("Add...")) {
306                            showEditor(false);
307                    } else if (c.equals("Add Comment...")) {
308                            showCommentEditor(false);
309                    } else if (c.equals("Add Separator")) {
310                            TextEntry newEntry = new TextEntry(null, this);
311                            textColumn.add(newEntry, textColumn.indexOf(selectedEntry));
312                            select(newEntry);
313                    } else if (c.equals("Modify...")) {
314                            if (selectedEntry.isComment()) {
315                                    showCommentEditor(true);
316                            } else {
317                                    showEditor(true);
318                            }
319                    } else if (c.equals("Delete")) {
320                            deleteSelectedEntry();
321                    } else if (c.equals("Cut")) {
322                            cutSelectedEntry();
323                    } else if (c.equals("Copy")) {
324                            copySelectedEntry();
325                    } else if (c.equals("Paste")) {
326                            pasteFromClipboard();
327                    } else if (c.equals("Expanded")) {
328                            selectedEntry.setExpanded(menuBar.isSelected("Expanded"));
329                    } else if (c.equals("Expand All")) {
330                            for (Component comp : textColumn.getComponents()) {
331                                    ((TextEntry) comp).setExpanded(true);
332                            }
333                    } else if (c.equals("Collapse All")) {
334                            for (Component comp : textColumn.getComponents()) {
335                                    ((TextEntry) comp).setExpanded(false);
336                            }
337                    } else if (c.startsWith("Show ")) {
338                            selectedEntry.setResultItemVisible(c.substring(5), menuBar.isSelected(c));
339                            selectedEntry.setExpanded(true);
340                    } else if (source instanceof PreditorWindow && c.matches("Cancel|Close|Escape")) {
341                            PreditorWindow preditor = (PreditorWindow) source;
342                            removeWindow(preditor);
343                            refreshKeyStrokeListener();
344                    } else if (source instanceof PreditorWindow && c.matches("OK|Enter")) {
345                            PreditorWindow preditor = (PreditorWindow) source;
346                            TextContainer textContainer = preditor.getTextContainer();
347                            if (textContainer.getTextElementsCount() == 0) {
348                                    removeWindow(preditor);
349                                    refreshKeyStrokeListener();
350                            } else {
351                                    if (preditor.isPossibleNextToken(".")) {
352                                            textContainer.addElement(new TextElement("."));
353                                    } else if (preditor.isPossibleNextToken("?")) {
354                                            textContainer.addElement(new TextElement("?"));
355                                    }
356                                    List<TextElement> l = textContainer.getTextElements();
357                                    if (l.isEmpty() || l.get(l.size() - 1).getText().matches("[.?]")) {
358                                            if (editMode) {
359                                                    selectedEntry.setText(textContainer.getText());
360                                                    select(selectedEntry);
361                                            } else {
362                                                    TextEntry newEntry = new TextEntry(
363                                                                    textContainer.getText(),
364                                                                    this,
365                                                                    menuBar.isSelected("Default Expanded")
366                                                            );
367                                                    for (String s : ResultItem.TYPES) {
368                                                            newEntry.setResultItemVisible(s, menuBar.isSelected("Default " + s));
369                                                    }
370                                                    textColumn.add(newEntry, textColumn.indexOf(selectedEntry));
371                                                    select(newEntry);
372                                            }
373                                            removeWindow(preditor);
374                                            refreshKeyStrokeListener();
375                                    } else if (c.equals("OK")) {
376                                            showWindow(new MessageWindow(
377                                                            "Error",
378                                                            "There are unfinished sentences.",
379                                                            "OK"
380                                                    ));
381                                    }
382                            }
383                    } else if (source instanceof TextAreaWindow && c.equals("OK")) {
384                            TextAreaWindow cew = (TextAreaWindow) source;
385                            if (editMode) {
386                                    selectedEntry.setText("# " + cew.getText());
387                                    select(selectedEntry);
388                            } else {
389                                    TextEntry newEntry = new TextEntry("# " + cew.getText(), this, false);
390                                    textColumn.add(newEntry, textColumn.indexOf(selectedEntry));
391                                    select(newEntry);
392                            }
393                    } else if (c.equals("Upload File")) {
394                            String fileContent = ((UploadWindow) source).getFileContent();
395                            if (fileContent != null) {
396                                    textColumn.removeAll();
397                                    String[] l = fileContent.replaceAll("\\s*(#[^\\n]*\\n)", "\n\n$1\n")
398                                                    .split("\\n[ \\t\\x0B\\f\\r]*\\n");
399                                    for (String line : l) {
400                                            TextEntry newEntry = new TextEntry(line, this, false);
401                                            textColumn.add(newEntry);
402                                            for (String s : ResultItem.TYPES) {
403                                                    newEntry.setResultItemVisible(s, menuBar.isSelected("Default " + s));
404                                            }
405                                    }
406                                    textColumn.add(finalEntry);
407                                    select((TextEntry) textColumn.getComponent(0));
408                            }
409                    } else if (c.equals("Load Lexicon") || c.equals("Replace Lexicon")) {
410                            String fileContent = ((UploadWindow) source).getFileContent();
411                            if (fileContent != null) {
412                                    if (c.equals("Replace Lexicon")) {
413                                            textColumn.removeAll();
414                                            textColumn.add(finalEntry);
415                                            select(finalEntry);
416                                            lexiconHandler.clear();
417                                    }
418                                    String[] l = (fileContent + " ").replaceAll("#[^\\n]*\\n", " ")
419                                                    .replaceAll("\\s+", " ").replaceFirst("^ ", "")
420                                                    .replaceAll("\\. ", ".\n").split("\\n");
421                                    for (String line : l) {
422                                            if (line.equals("")) continue;
423                                            lexiconHandler.addWord(line);
424                                    }
425    
426                                    if (c.equals("Replace Lexicon")) {
427                                            showWindow(new MessageWindow(
428                                                            "Lexicon Replaced",
429                                                            "The lexicon has been replaced.",
430                                                            "OK"
431                                                    ));
432                                    } else {
433                                            showWindow(new MessageWindow(
434                                                            "Lexicon Loaded",
435                                                            "The lexicon has been loaded.",
436                                                            "OK"
437                                                    ));
438                                    }
439                            }
440                    } else if (c.equals("Up Pressed")) {
441                            int i = textColumn.indexOf(selectedEntry);
442                            if (i > 0) {
443                                    select((TextEntry) textColumn.getComponent(i-1));
444                            }
445                    } else if (c.equals("Down Pressed")) {
446                            int i = textColumn.indexOf(selectedEntry);
447                            if (i < textColumn.getComponentCount()-1) {
448                                    select((TextEntry) textColumn.getComponent(i+1));
449                            }
450                    } else if (c.equals("Space Pressed")) {
451                            if (selectedEntry.isEmpty()) {
452                                    showEditor(false);
453                            } else {
454                                    if (selectedEntry.isExpanded()) {
455                                            selectedEntry.setExpanded(false);
456                                    } else {
457                                            selectedEntry.setExpanded(true);
458                                    }
459                            }
460                    } else if (c.equals("Backspace Pressed")) {
461                            deleteSelectedEntry();
462                    } else if (c.equals("Func-A Pressed")) {
463                            showEditor(false);
464                    } else if (c.equals("Func-M Pressed")) {
465                            if (selectedEntry.isComment()) {
466                                    showCommentEditor(true);
467                            } else {
468                                    showEditor(true);
469                            }
470                    } else if (c.equals("Func-X Pressed")) {
471                            cutSelectedEntry();
472                    } else if (c.equals("Func-C Pressed")) {
473                            copySelectedEntry();
474                    } else if (c.equals("Func-V Pressed")) {
475                            pasteFromClipboard();
476                    } else if (c.equals("Func-O Pressed")) {
477                            openFile();
478                    } else if (c.equals("Func-S Pressed")) {
479                            saveFile();
480                    }
481            }
482    
483            private void refreshKeyStrokeListener() {
484                    // The different keystroke listeners somehow interfere with each other so that this
485                    // work-around is needed:
486    //              mainColumn.remove(keyStrokeListener);
487    //              mainColumn.add(keyStrokeListener);
488            }
489    
490            private void showEditor(boolean edit) {
491                    if (edit && selectedEntry.isEmpty()) return;
492                    
493                    ACEEditorMenuCreator menuCreator = new ACEEditorMenuCreator(this, lexiconHandler);
494                    ChartParser cp = new ChartParser(ACEEditorGrammar.grammar, "text");
495                    cp.setDynamicLexicon(lexiconHandler);
496                    PreditorWindow preditor = new PreditorWindow("ACE Text Editor", cp);
497                    preditor.setMenuCreator(menuCreator);
498                    menuCreator.setPreditorWindow(preditor);
499                    preditor.addActionListener(this);
500                    this.editMode = edit;
501                    if (edit) {
502                            preditor.addText(selectedEntry.getText() + " ");
503                    }
504                    showWindow(preditor);
505            }
506    
507            private void showCommentEditor(boolean edit) {
508                    this.editMode = edit;
509                    if (edit) {
510                            showWindow(new TextAreaWindow(
511                                            "Comment Editor",
512                                            selectedEntry.getText().substring(2),
513                                            this
514                                    ));
515                    } else {
516                            showWindow(new TextAreaWindow("Comment Editor", "", this));
517                    }
518            }
519    
520            private void deleteSelectedEntry() {
521                    if (selectedEntry != finalEntry) {
522                            int i = textColumn.indexOf(selectedEntry);
523                            TextEntry nextEntry = (TextEntry) textColumn.getComponent(i+1);
524                            textColumn.remove(selectedEntry);
525                            select(nextEntry);
526                    }
527            }
528    
529            private void copySelectedEntry() {
530                    clipboard = selectedEntry.copy();
531                    menuBar.setEnabled("Paste", true);
532                    menuBar.update();
533            }
534    
535            private void cutSelectedEntry() {
536                    if (selectedEntry != finalEntry) {
537                            copySelectedEntry();
538                            deleteSelectedEntry();
539                    }
540            }
541    
542            private void pasteFromClipboard() {
543                    if (clipboard != null) {
544                            TextEntry newEntry = clipboard.copy();
545                            textColumn.add(newEntry, textColumn.indexOf(selectedEntry));
546                            select(newEntry);
547                    }
548            }
549    
550            private void openFile() {
551                    UploadWindow uw = new UploadWindow(
552                                    "Open File",
553                                    "Warning: This will delete the current content.\nChoose a file to open:",
554                                    null,
555                                    this
556                    );
557                    uw.setActionCommand("Upload File");
558                    uw.setMaxFileSize(getMaxUploadFileSize());
559                    showWindow(uw);
560            }
561    
562            private void saveFile() {
563                    final String f = getFullText();
564                    AbstractDownloadProvider provider = new AbstractDownloadProvider() {
565                            
566                            private static final long serialVersionUID = 898782345234987345L;
567    
568                            public String getContentType() {
569                                    return "text/plain";
570                            }
571    
572                            public String getFileName() {
573                                    return "text.ace.txt";
574                            }
575    
576                            public long getSize() {
577                                    return f.length();
578                            }
579    
580                            public void writeFile(OutputStream out) throws IOException {
581                                    out.write(f.getBytes());
582                                    out.close();
583                            }
584    
585                    };
586                    getApplicationInstance().enqueueCommand(new DownloadCommand(provider));
587            }
588    
589            private void loadLexicon(boolean replace) {
590                    String title = "Load Lexicon";
591                    String message = "Choose a lexicon file to load:";
592                    String actionCommand = "Load Lexicon";
593                    if (replace) {
594                            title = "Replace Lexicon";
595                            message = "Warning: This will delete the current content.\n" + message;
596                            actionCommand = "Replace Lexicon";
597                    }
598                    UploadWindow uw = new UploadWindow(title, message, null, this);
599                    uw.setActionCommand(actionCommand);
600                    uw.setMaxFileSize(getMaxUploadFileSize());
601                    showWindow(uw);
602            }
603    
604            private void saveLexicon() {
605                    final String f = lexiconHandler.getLexiconFileContent();
606                    AbstractDownloadProvider provider = new AbstractDownloadProvider() {
607                            
608                            private static final long serialVersionUID = 1932606314346272768L;
609    
610                            public String getContentType() {
611                                    return "text/plain";
612                            }
613    
614                            public String getFileName() {
615                                    return "text.lex.pl";
616                            }
617    
618                            public long getSize() {
619                                    return f.length();
620                            }
621    
622                            public void writeFile(OutputStream out) throws IOException {
623                                    out.write(f.getBytes());
624                                    out.close();
625                            }
626    
627                    };
628                    getApplicationInstance().enqueueCommand(new DownloadCommand(provider));
629            }
630    
631            /**
632             * Returns information about ACE Editor, like the version number and the release date. This
633             * information is read from the file "aceeditor.properties".
634             * 
635             * @param key The key string.
636             * @return The value for the given key.
637             */
638            public static String getInfo(String key) {
639                    if (properties == null) {
640                            String f = "ch/uzh/ifi/attempto/aceeditor/aceeditor.properties";
641                            InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(f);
642                            properties = new Properties();
643                            try {
644                                    properties.load(in);
645                            } catch (Exception ex) {
646                                    ex.printStackTrace();
647                            }
648                    }
649    
650                    return properties.getProperty(key);
651            }
652    
653    }