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 }