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 }