package ch.uzh.ifi.attempto.acewiki.gf; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import ch.uzh.ifi.attempto.acewiki.core.ModuleElement; import ch.uzh.ifi.attempto.acewiki.core.Ontology; import ch.uzh.ifi.attempto.acewiki.core.OntologyElement; import com.google.common.collect.HashBasedTable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Table; import nextapp.echo.app.table.AbstractTableModel; import nextapp.echo.app.table.TableModel; /** * <p>Represents a set of the GF modules as a table where rows are functions, columns are the modules, * and each cell contains the linearization definition of the given function in the given module. * The table is compiled only from the source files.</p> * * <p>Note that we only consider modules whose source has a certain simple structure which * we can easily parse with a regex. The module must be a concrete module with a single "lin" * on its own line, separating the "header" of the module from the linearization definitions.</p> * * @author Kaarel Kaljurand */ public class GfLexiconEditorModel extends AbstractTableModel implements TableModel { private static final long serialVersionUID = -2494830121762821312L; // TODO: using ; in the linearization definition is not supported private static final Pattern PATTERN_LIN = Pattern.compile("\\s*([A-Za-z_][A-Za-z0-9_']*)\\s*=([^;]+);"); private static final Pattern PATTERN_SPLIT = Pattern.compile("^(.*concrete.+of.+\\nlin\\n)(.*)}\\s*$", Pattern.DOTALL); private final Table<String, ModuleElement, String> funToModuleToLin = HashBasedTable.create(); private final List<String> mFuns; private final List<ModuleElement> mModules; private final Map<ModuleElement, String> mModuleToHeader = Maps.newHashMap(); public GfLexiconEditorModel(Ontology ont, final String language) { OntologyElement oe = ont.getElement(language); final ModuleElement languageModule; if (oe != null && oe instanceof ModuleElement) { languageModule = (ModuleElement) oe; } else { languageModule = null; } for (ModuleElement gfModule : ont.getOntologyElements(ModuleElement.class)) { refreshModuleElement(gfModule); } mFuns = Lists.newArrayList(funToModuleToLin.rowKeySet()); mModules = Lists.newArrayList(funToModuleToLin.columnKeySet()); Collections.sort(mFuns, String.CASE_INSENSITIVE_ORDER); /* * We sort the columns so that the module for the selected language comes first. * If no module corresponds to the selected language then prefer modules that * the selected language module references. This handles incomplete modules * which are referenced by concrete modules. */ Collections.sort(mModules, new Comparator<ModuleElement>() { @Override public int compare(ModuleElement arg1, ModuleElement arg2) { if (languageModule.equals(arg1)) { return -1; } if (languageModule.equals(arg2)) { return 1; } if (languageModule.references(arg1)) { return -1; } if (languageModule.references(arg2)) { return 1; } String moduleName1 = arg1.getWord(); String moduleName2 = arg2.getWord(); return moduleName1.compareTo(moduleName2); } }); } @Override public int getColumnCount() { return 1 + mModules.size(); } @Override public int getRowCount() { return mFuns.size(); } @Override public Object getValueAt(int column, int row) { String fun = mFuns.get(row); if (column == 0) { return fun; } return funToModuleToLin.get(fun, getModuleElement(column)); } @Override public String getColumnName(int column) { if (column == 0) { return null; } return getModuleElement(column).getWord(); } public void setValueAt(Object value, int column, int row) { ModuleElement moduleElement = getModuleElement(column); refreshModuleElement(moduleElement); String header = mModuleToHeader.get(moduleElement); if (header != null) { if (value == null || value.toString().trim().isEmpty()) { funToModuleToLin.remove(mFuns.get(row), moduleElement); } else { funToModuleToLin.put(mFuns.get(row), moduleElement, value.toString().trim()); } moduleElement.replaceModuleContent(header + makeModuleSource(moduleElement) + "}"); } fireTableCellUpdated(column, row); } /** * @param column table column * @return module element that corresponds to the given column */ public ModuleElement getModuleElement(int column) { if (column > 0 && column <= mModules.size()) { return mModules.get(column - 1); } else { throw new IllegalArgumentException(); } } public List<ModuleElement> getModules() { return ImmutableList.copyOf(mModules); } private boolean refreshModuleElement(ModuleElement gfModule) { String content = gfModule.getModuleContent().getText(); Matcher matcherSplit = PATTERN_SPLIT.matcher(content); if (! matcherSplit.matches()) { mModuleToHeader.remove(gfModule); return false; } mModuleToHeader.put(gfModule, matcherSplit.group(1)); Matcher matcherLins = PATTERN_LIN.matcher(matcherSplit.group(2)); while (matcherLins.find()) { String fun = matcherLins.group(1); String lin = matcherLins.group(2).trim(); funToModuleToLin.put(fun, gfModule, lin); } return true; } /** * <p>For the given module, return its linearization definitions. * If a definition is missing for a function then the respective entry is not included.</p> */ private StringBuilder makeModuleSource(ModuleElement module) { StringBuilder sb = new StringBuilder(); for (String fun : mFuns) { Object value = funToModuleToLin.get(fun, module); if (value != null && ! "".equals(value.toString())) { sb.append(fun); sb.append(" = "); sb.append(value); sb.append(" ;\n"); } } return sb; } }