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