    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.preditor;
    016
    017    import java.util.ArrayList;
    018    import java.util.HashMap;
    019    import java.util.List;
    020
    021    import ch.uzh.ifi.attempto.chartparser.ChartParser;
    022    import ch.uzh.ifi.attempto.chartparser.Terminal;
    023    import ch.uzh.ifi.attempto.preditor.text.RefTextElement;
    024    import ch.uzh.ifi.attempto.preditor.text.RefableTextElement;
    025    import ch.uzh.ifi.attempto.preditor.text.TextContainer;
    026    import ch.uzh.ifi.attempto.preditor.text.TextElement;
    027    import ch.uzh.ifi.attempto.preditor.text.VarTextElement;
    028
    029    /**
    030     * This abstract class is used for the predictive editor to create menus on the basis of categories of the grammar.
    031     *
    032     * @author Tobias Kuhn
    033     */
    034    public abstract class MenuCreator {
    035
    036            private HashMap<String, MenuBlockContent> contentsMap = new HashMap<String, MenuBlockContent>();
    037            private List<MenuBlockContent> contentsList = new ArrayList<MenuBlockContent>();
    038            private ChartParser parser;
    039            private TextContainer textContainer;
    040
    041            /**
    042             * Initializes a new menu creator object.
    043             */
    044            public MenuCreator() {
    045            }
    046
    047            /**
    048             * An implementation of this abstract method should process the category by calling {@link #addMenuItem addMenuItem}
    049             * and/or {@link #addMenuEntry addMenuEntry}.
    050             *
    051             * @param category The category of the grammar to be processed.
    052             */
    053            public abstract void processCategory(Terminal category);
    054
    055            /**
    056             * This abstract method is called when the predictive editor is refreshed (before the first call of
    057             * {@link #processCategory processCategory}). This is a good place for calling {@link #prepareMenuBlock prepareMenuBlock}.
    058             */
    059            public abstract void initMenuCreation();
    060
    061            /**
    062             * Prepares a new menu block (if there is no such menu block already with the same name). The menu blocks are
    063             * shown in the predictive editor in the same order as they have been prepared.
    064             *
    065             * @param menuBlockName The name of the menu block.
    066             */
    067            public void prepareMenuBlock(String menuBlockName) {
    068                    prepareMenuBlock(menuBlockName, false);
    069            }
    070
    071            /**
    072             * Prepares a new menu block (if there is no such menu block already with the same name). The second parameter
    073             * defines whether the items of the menu block should be sorted. The menu blocks are
    074             * shown in the predictive editor in the same order as they have been prepared.
    075             *
    076             * @param menuBlockName The name of the menu block.
    077             * @param doSort true if the items should be sorted.
    078             */
    079            public void prepareMenuBlock(String menuBlockName, boolean doSort) {
    080                    if (contentsMap.containsKey(menuBlockName)) {
    081                            return;
    082                    }
    083                    MenuBlockContent menuBlockContent = new MenuBlockContent(menuBlockName, doSort);
    084                    contentsMap.put(menuBlockName, menuBlockContent);
    085                    contentsList.add(menuBlockContent);
    086            }
    087
    088            /**
    089             * Adds the menu item to the given menu block. If no menu block of this name has been prepared at this point then
    090             * the preparation is done first.
    091             *
    092             * @param menuBlockName The name of the menu block.
    093             * @param menuItem The menu item to be added to the menu block.
    094             */
    095            public void addMenuItem(String menuBlockName, MenuItem menuItem) {
    096                    if (!contentsMap.containsKey(menuBlockName)) {
    097                            prepareMenuBlock(menuBlockName);
    098                    }
    099                    contentsMap.get(menuBlockName).addItem(menuItem);
    100            }
    101
    102            /**
    103             * Adds a new menu entry containing the text element to the menu block. If no menu block of this name has been
    104             * prepared at this point then the preparation is done first.
    105             *
    106             * @param menuBlockName The name of the menu block.
    107             * @param textElement The text element of the menu entry that is created and then added to the menu block.
    108             */
    109            public void addMenuEntry(String menuBlockName, TextElement textElement) {
    110                    if (!contentsMap.containsKey(menuBlockName)) {
    111                            prepareMenuBlock(menuBlockName);
    112                    }
    113                    contentsMap.get(menuBlockName).addEntry(textElement);
    114            }
    115
    116            /**
    117             * This method looks for referenceable text elements earlier in the token list, given the category of the next
    118             * token. For each of those text elements, a new reference text element is created and added to the menu block.
    119             *
    120             * @param menuBlockName The name of the menu block.
    121             * @param category The category name for which the previous tokens should be checked for referenceability.
    122             * @see #getAccessibleTextElements(String)
    123             * @see RefableTextElement
    124             * @see RefTextElement
    125             */
    126            public void addReferenceEntries(String menuBlockName, String category) {
    127                    ArrayList<String> refStrings = new ArrayList<String>();
    128                    for (TextElement el : getAccessibleTextElements(category)) {
    129                            if (!(el instanceof RefableTextElement)) continue;
    130
    131                            RefableTextElement refEl = (RefableTextElement) el;
    132                            boolean isOverridden = false;
    133                            String refText = refEl.getReferenceText();
    134                            for (String s : refStrings) {
    135                                    if (s.equals(refText) || s.startsWith(refText + " ")) {
    136                                            isOverridden = true;
    137                                            break;
    138                                    }
    139                            }
    140                            if (!isOverridden) {
    141                                    refStrings.add(refText);
    142                                    addMenuEntry(menuBlockName, new RefTextElement(refEl, category));
    143                            }
    144                    }
    145            }
    146
    147            /**
    148             * This method adds text elements that contain variables (in the form of "X", "Y", "Z", "X1", "Y1", "Z1", "X2", and so on)
    149             * to the menu block.
    150             *
    151             * @param menuBlockName The name of the menu block.
    152             * @param num Variables are created up to this number.
    153             * @param category The category name of the variable tokens.
    154             * @see VarTextElement
    155             */
    156            public void addVariableEntries(String menuBlockName, int num, String category) {
    157                    ArrayList<String> varNames = new ArrayList<String>();
    158                    varNames.add("X");
    159                    varNames.add("Y");
    160                    varNames.add("Z");
    161                    for (int i=1; i<=num; i++) {
    162                            varNames.add("X" + i);
    163                            varNames.add("Y" + i);
    164                            varNames.add("Z" + i);
    165                    }
    166                    for (TextElement el : textContainer.getTextElements()) {
    167                            if (!(el instanceof VarTextElement)) continue;
    168                            varNames.remove(((VarTextElement) el).getText());
    169                    }
    170                    for (String s : varNames) {
    171                            addMenuEntry(menuBlockName, new VarTextElement(s, category));
    172                    }
    173            }
    174
    175            /**
    176             * This method returns all text elements that are accessible for the given next token.
    177             *
    178             * @param nextToken The next token.
    179             * @return A list of all accessible text elements.
    180             */
    181            public List<TextElement> getAccessibleTextElements(String nextToken) {
    182                    List<TextElement> elements = new ArrayList<TextElement>();
    183
    184                    boolean[] pos = parser.getAccessiblePositions(nextToken);
    185                    for (int i=pos.length-1 ; i >= 0 ; i--) {
    186                            if (!pos[i]) continue;
    187                            TextElement el = textContainer.getTextElements().get(i);
    188                            elements.add(el);
    189                    }
    190
    191                    return elements;
    192            }
    193
    194            List<MenuBlockContent> createMenu(ChartParser parser, TextContainer textContainer) {
    195                    this.parser = parser;
    196                    this.textContainer = textContainer;
    197                    contentsMap.clear();
    198                    contentsList.clear();
    199
    200                    initMenuCreation();
    201
    202                    for (Terminal t : parser.nextTokens()) {
    203                            processCategory(t);
    204                    }
    205
    206                    return new ArrayList<MenuBlockContent>(contentsList);
    207            }
    208
    209    }