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    }