Java tutorial
/* * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package annis.gui.flatquerybuilder; import annis.gui.QueryController; import annis.gui.objects.Query; import annis.libgui.Helper; import annis.service.objects.AnnisAttribute; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.client.WebResource; import com.vaadin.data.Property; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.MenuBar; import com.vaadin.ui.NativeSelect; import com.vaadin.ui.Notification; import com.vaadin.ui.Panel; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ChameleonTheme; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* * @author martin klotz (martin.klotz@hu-berlin.de) * @author tom ruette (tom.ruette@hu-berlin.de) */ public class FlatQueryBuilder extends Panel implements Button.ClickListener, Property.ValueChangeListener { private static final Logger log = LoggerFactory.getLogger(FlatQueryBuilder.class); private Button btGo; private Button btClear; private Button btInverse; private Button btInitSpan; private Button btInitMeta; private Button btInitLanguage; private MenuBar addMenu; private MenuBar addMenuSpan; private MenuBar addMenuMeta; private QueryController cp; private HorizontalLayout language; private HorizontalLayout languagenodes; private HorizontalLayout span; private HorizontalLayout meta; private HorizontalLayout toolbar; private HorizontalLayout advanced; private VerticalLayout mainLayout; private NativeSelect filtering; private Collection<VerticalNode> vnodes; private Collection<EdgeBox> eboxes; private Collection<MetaBox> mboxes; private SpanBox spbox; private String query; private MenuBar.MenuItem spanMenu; private ReducingStringComparator rsc; private static final String[] REGEX_CHARACTERS = { "\\", "+", ".", "[", "*", "^", "$", "|", "?", "(", ")" }; private static final String BUTTON_GO_LABEL = "Create AQL Query"; private static final String BUTTON_CLEAR_LABEL = "Clear the Query Builder"; private static final String BUTTON_INV_LABEL = "Refresh Query Builder"; private static final String NO_CORPORA_WARNING = "No corpora selected, please select " + "at least one corpus."; private static final String INCOMPLETE_QUERY_WARNING = "Query seems to be incomplete."; private static final String QUERY_ERROR_WARNING = "An Error occured. Please check your query."; private static final String ADD_LING_PARAM = "Add"; private static final String ADD_SPAN_PARAM = "Add"; private static final String CHANGE_SPAN_PARAM = "Change"; private static final String ADD_META_PARAM = "Add"; private static final String INFO_INIT_LANG = "In this part of the Query Builder, " + "blocks of the linguistic query can be constructed from left to right."; private static final String INFO_INIT_SPAN = "This part of the Query Builder " + "allows you to define a span annotation within which the above query blocks " + "are confined."; private static final String INFO_INIT_META = "Here, you can constrain the linguistic " + "query by selecting meta levels."; private static final String INFO_FILTER = "When searching in the fields, the " + "hits are sorted and filtered according to different mechanisms. Please " + "choose a filtering mechanism here."; private static final String TOOLBAR_CAPTION = "Toolbar"; private static final String META_CAPTION = "Meta information"; private static final String SPAN_CAPTION = "Scope"; private static final String LANG_CAPTION = "Linguistic sequence"; private static final String ADVANCED_CAPTION = "Advanced settings"; public FlatQueryBuilder(QueryController cp) { setSizeFull(); launch(cp); } private void launch(QueryController cp) { this.cp = cp; rsc = new ReducingStringComparator(); this.query = ""; mainLayout = new VerticalLayout(); // tracking lists for vertical nodes, edgeboxes and metaboxes vnodes = new ArrayList<>(); eboxes = new ArrayList<>(); mboxes = new ArrayList<>(); spbox = null; // buttons and checks btGo = new Button(BUTTON_GO_LABEL, (Button.ClickListener) this); btGo.setStyleName(ChameleonTheme.BUTTON_SMALL); btClear = new Button(BUTTON_CLEAR_LABEL, (Button.ClickListener) this); btClear.setStyleName(ChameleonTheme.BUTTON_SMALL); btInverse = new Button(BUTTON_INV_LABEL, (Button.ClickListener) this); btInverse.setStyleName(ChameleonTheme.BUTTON_SMALL); btInitLanguage = new Button("Initialize", (Button.ClickListener) this); btInitLanguage.setDescription(INFO_INIT_LANG); btInitSpan = new Button("Initialize", (Button.ClickListener) this); btInitSpan.setDescription(INFO_INIT_SPAN); btInitMeta = new Button("Initialize", (Button.ClickListener) this); btInitMeta.setDescription(INFO_INIT_META); filtering = new NativeSelect("Filtering mechanisms"); filtering.setDescription(INFO_FILTER); ReducingStringComparator rdc = new ReducingStringComparator(); Set mappings = rdc.getMappings().keySet(); int i; for (i = 0; i < mappings.size(); i++) { String mapname = (String) mappings.toArray()[i]; filtering.addItem(i); filtering.setItemCaption(i, mapname); } filtering.addItem(i + 1); filtering.setItemCaption(i + 1, "generic"); filtering.select(i + 1); filtering.setNullSelectionAllowed(false); filtering.setImmediate(true); // language layout language = new HorizontalLayout(); languagenodes = new HorizontalLayout(); language.addComponent(languagenodes); language.addComponent(btInitLanguage); language.setMargin(true); language.setCaption(LANG_CAPTION); language.addStyleName("linguistics-panel"); // span layout span = new HorizontalLayout(); span.setSpacing(true); span.addComponent(btInitSpan); span.setMargin(true); span.setCaption(SPAN_CAPTION); span.addStyleName("span-panel"); // meta layout meta = new HorizontalLayout(); meta.setSpacing(true); meta.addComponent(btInitMeta); meta.setMargin(true); meta.setCaption(META_CAPTION); meta.addStyleName("meta-panel"); // toolbar layout toolbar = new HorizontalLayout(); toolbar.setSpacing(true); toolbar.addComponent(btGo); toolbar.addComponent(btClear); toolbar.addComponent(btInverse); toolbar.setMargin(true); toolbar.setCaption(TOOLBAR_CAPTION); toolbar.addStyleName("toolbar-panel"); // advanced advanced = new HorizontalLayout(); advanced.setSpacing(true); advanced.addComponent(filtering); advanced.setMargin(true); advanced.setCaption(ADVANCED_CAPTION); advanced.addStyleName("advanced-panel"); // put everything on the layout mainLayout.setSpacing(true); mainLayout.addComponent(language); mainLayout.addComponent(span); mainLayout.addComponent(meta); mainLayout.addComponent(toolbar); mainLayout.addComponent(advanced); setContent(mainLayout); getContent().setWidth("100%"); getContent().setHeight("-1px"); } @Override public void valueChange(Property.ValueChangeEvent event) { initialize(); } @Override public void attach() { super.attach(); cp.getState().getSelectedCorpora().addValueChangeListener(this); } @Override public void detach() { super.detach(); cp.getState().getSelectedCorpora().removeValueChangeListener(this); } private void initialize() { // try to remove all existing menus try { language.removeComponent(addMenu); span.removeComponent(addMenuSpan); meta.removeComponent(addMenuMeta); } catch (Exception e) { log.error(null, e); } //init variables: final FlatQueryBuilder sq = this; Collection<String> annonames = getAvailableAnnotationNames(); Collection<String> metanames = getAvailableMetaNames(); //Code from btInitLanguage: addMenu = new MenuBar(); //addMenu.setDescription(INFO_INIT_LANG); addMenu.setAutoOpen(false); final MenuBar.MenuItem add = addMenu.addItem(ADD_LING_PARAM, null); for (final String annoname : annonames) { add.addItem(annoname, new MenuBar.Command() { @Override public void menuSelected(MenuBar.MenuItem selectedItem) { if (!vnodes.isEmpty()) { EdgeBox eb = new EdgeBox(sq); languagenodes.addComponent(eb); eboxes.add(eb); } VerticalNode vn = new VerticalNode(annoname, sq); languagenodes.addComponent(vn); vnodes.add(vn); addMenu.setAutoOpen(false); } }); } language.removeComponent(btInitLanguage); language.addComponent(addMenu); //Code from btInitSpan: addMenuSpan = new MenuBar(); //addMenuSpan.setDescription(INFO_INIT_SPAN); addMenuSpan.setAutoOpen(false); final MenuBar.MenuItem addSpan = addMenuSpan.addItem(ADD_SPAN_PARAM, null); for (final String annoname : annonames) { addSpan.addItem(annoname, new MenuBar.Command() { @Override public void menuSelected(MenuBar.MenuItem selectedItem) { sq.removeSpanBox(); sq.addSpanBox(annoname); addMenuSpan.setAutoOpen(false); } }); } spanMenu = addSpan; span.removeComponent(btInitSpan); span.addComponent(addMenuSpan); //Code from btInitMeta: addMenuMeta = new MenuBar(); //addMenuMeta.setDescription(INFO_INIT_META); addMenuMeta.setAutoOpen(false); final MenuBar.MenuItem addMeta = addMenuMeta.addItem(ADD_META_PARAM, null); int i = 0; for (final String annoname : metanames) { addMeta.addItem(annoname, new MenuBar.Command() { @Override public void menuSelected(MenuBar.MenuItem selectedItem) { MetaBox mb = new MetaBox(annoname, sq); meta.addComponent(mb); mboxes.add(mb); addMenuMeta.setAutoOpen(false); //addMeta.removeChild(selectedItem); selectedItem.setVisible(false); } }); } meta.removeComponent(btInitMeta); meta.addComponent(addMenuMeta); } private String getAQLFragment(SearchBox sb) { String result = ""; String value = null; try { value = sb.getValue(); } catch (java.lang.NullPointerException ex) { value = null; } String level = sb.getAttribute(); if (value == null) { result = level; } if (value != null) { if (sb.isRegEx() && !sb.isNegativeSearch()) { result = level + "=/" + value.replace("/", "\\x2F") + "/"; } if (sb.isRegEx() && sb.isNegativeSearch()) { result = level + "!=/" + value.replace("/", "\\x2F") + "/"; } if (!sb.isRegEx() && sb.isNegativeSearch()) { result = level + "!=\"" + value.replace("\"", "\\x22") + "\""; } if (!sb.isRegEx() && !sb.isNegativeSearch()) { result = level + "=\"" + value.replace("\"", "\\x22") + "\""; } } return result; } private String getMetaQueryFragment(MetaBox mb) { Collection<String> values = mb.getValues(); if (!values.isEmpty()) { StringBuilder result = new StringBuilder("\n& meta::" + mb.getMetaDatum() + " = "); if (values.size() == 1) { result.append("\"" + values.iterator().next().replace("\"", "\\x22") + "\""); } else { Iterator<String> itValues = values.iterator(); result.append("/(" + escapeRegexCharacters(itValues.next()) + ")"); while (itValues.hasNext()) { result.append("|(" + escapeRegexCharacters(itValues.next()) + ")"); } result.append("/"); } return result.toString(); } return ""; } public String escapeRegexCharacters(String tok) { if (tok == null) { return ""; } if (tok.equals("")) { return ""; } String result = tok; for (int i = 0; i < REGEX_CHARACTERS.length; i++) { result = result.replace(REGEX_CHARACTERS[i], "\\" + REGEX_CHARACTERS[i]); } return result.replace("/", "\\x2F"); } public String unescape(String s) { //first unescape slashes and quotes: s = unescapeSlQ(s); //unescape regex characters: int i = 1; while (i < s.length()) { char c0 = s.charAt(i - 1); char c1 = s.charAt(i); for (int j = 0; j < REGEX_CHARACTERS.length; j++) { if ((c1 == REGEX_CHARACTERS[j].charAt(0)) & (c0 == '\\')) { s = s.substring(0, i - 1) + s.substring(i); break; } if (j == REGEX_CHARACTERS.length - 1) { i++; } } } return s; } public String unescapeSlQ(String s) { return s.replace("\\x2F", "/").replace("\\x22", "\""); } private String getAQLQuery() { int count = 1; StringBuilder ql = new StringBuilder(); StringBuilder edgeQuery = new StringBuilder(); StringBuilder sentenceQuery = new StringBuilder(); Collection<Integer> sentenceVars = new ArrayList<>(); Iterator<EdgeBox> itEboxes = eboxes.iterator(); for (VerticalNode v : vnodes) { Collection<SearchBox> sboxes = v.getSearchBoxes(); for (SearchBox s : sboxes) { ql.append(" & " + getAQLFragment(s)); } if (sboxes.isEmpty()) { //not sure we want to do it this way: ql.append("\n& /.*/"); } sentenceVars.add(Integer.valueOf(count)); for (int i = 1; i < sboxes.size(); i++) { String addQuery = "\n& #" + count + "_=_" + "#" + ++count; edgeQuery.append(addQuery); } count++; String edgeQueryAdds = (itEboxes.hasNext()) ? "\n& #" + (count - 1) + " " + itEboxes.next().getValue() + " #" + count : ""; edgeQuery.append(edgeQueryAdds); } String addQuery = ""; try { SpanBox spb = (SpanBox) span.getComponent(1); if ((!spb.isRegEx()) && (!spb.getValue().isEmpty())) { addQuery = "\n& " + spb.getAttribute() + " = \"" + spb.getValue() + "\""; } if (spb.isRegEx()) { addQuery = "\n& " + spb.getAttribute() + " = /" + spb.getValue().replace("/", "\\x2F") + "/"; } if (spb.getValue().isEmpty()) { addQuery = "\n&" + spb.getAttribute(); } ql.append(addQuery); for (Integer i : sentenceVars) { sentenceQuery.append("\n& #").append(count).append("_i_#").append(i.toString()); } } catch (Exception ex) { ex = null; } StringBuilder metaQuery = new StringBuilder(); Iterator<MetaBox> itMetaBoxes = mboxes.iterator(); while (itMetaBoxes.hasNext()) { metaQuery.append(getMetaQueryFragment(itMetaBoxes.next())); } String fullQuery = (ql.toString() + edgeQuery.toString() + sentenceQuery.toString() + metaQuery.toString()); if (fullQuery.length() < 3) { return ""; } fullQuery = fullQuery.substring(3);//deletes leading " & " this.query = fullQuery; return fullQuery; } public void updateQuery() { try { cp.setQuery(new Query(getAQLQuery(), cp.getState().getSelectedCorpora().getValue())); } catch (java.lang.NullPointerException ex) { Notification.show(INCOMPLETE_QUERY_WARNING); } } @Override public void buttonClick(Button.ClickEvent event) { if (cp.getState().getSelectedCorpora().getValue().isEmpty()) { Notification.show(NO_CORPORA_WARNING); } else { if (event.getButton() == btGo) { updateQuery(); } if (event.getButton() == btClear) { clear(); updateQuery(); launch(cp); } if (event.getComponent() == btInverse) { try { loadQuery(); } catch (EmptyReferenceException e) { log.error(null, e); } catch (EqualityConstraintException e) { log.error(null, e); } catch (InvalidCharacterSequenceException e) { log.error(null, e); } catch (MultipleAssignmentException e) { log.error(null, e); } catch (UnknownLevelException e) { log.error(null, e); } } if (event.getComponent() == btInitMeta || event.getComponent() == btInitSpan || event.getComponent() == btInitLanguage) { initialize(); } } } public void removeVerticalNode(VerticalNode v) { Iterator<VerticalNode> itVnodes = vnodes.iterator(); Iterator<EdgeBox> itEboxes = eboxes.iterator(); VerticalNode vn = itVnodes.next(); EdgeBox eb = null; while (!vn.equals(v)) { vn = itVnodes.next(); eb = itEboxes.next(); } if ((eb == null) & (itEboxes.hasNext())) { eb = itEboxes.next(); } vnodes.remove(v); if (eb != null) { eboxes.remove(eb); languagenodes.removeComponent(eb); } languagenodes.removeComponent(v); } public void addSpanBox(String level) { spbox = new SpanBox(level, this); span.addComponent(spbox); span.setComponentAlignment(spbox, Alignment.MIDDLE_LEFT); spanMenu.setText(CHANGE_SPAN_PARAM); } public void addSpanBox(SpanBox spb) { if (spbox == null) { spanMenu.setText(CHANGE_SPAN_PARAM); } else { span.removeComponent(spbox); } spbox = spb; span.addComponent(spbox); span.setComponentAlignment(spbox, Alignment.MIDDLE_LEFT); } public void removeSpanBox() { if (spbox != null) { span.removeComponent(spbox); spbox = null; spanMenu.setText(ADD_SPAN_PARAM); } } public void removeSpanBox(SpanBox spb) { if (spb.equals(spbox)) { removeSpanBox(); } } public void removeMetaBox(MetaBox v) { meta.removeComponent(v); mboxes.remove(v); List<MenuBar.MenuItem> items = addMenuMeta.getItems().get(0).getChildren(); boolean found = false; String metalevel = v.getMetaDatum(); for (int i = 0; (i < items.size()) & !found; i++) { MenuBar.MenuItem itm = items.get(i); if (itm.getText().equals(metalevel)) { itm.setVisible(true); found = true; } } } public Collection<String> getAnnotationValues(String level) { Collection<String> values = new TreeSet<>(); for (String s : getAvailableAnnotationLevels(level)) { values.add(s); } return values; } public Set<String> getAvailableAnnotationNames() { Set<String> result = new TreeSet<>(); WebResource service = Helper.getAnnisWebResource(); // get current corpus selection Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue(); if (service != null) { try { List<AnnisAttribute> atts = new LinkedList<>(); for (String corpus : corpusSelection) { atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations") .queryParam("fetchvalues", "false").queryParam("onlymostfrequentvalues", "false") .get(new GenericType<List<AnnisAttribute>>() { })); } for (AnnisAttribute a : atts) { if (a.getType() == AnnisAttribute.Type.node) { result.add(killNamespace(a.getName())); } } } catch (ClientHandlerException ex) { log.error(null, ex); } catch (UniformInterfaceException ex) { log.error(null, ex); } } result.add("tok"); return result; } public Collection<String> getAvailableAnnotationLevels(String meta) { Collection<String> result = new TreeSet<>(); WebResource service = Helper.getAnnisWebResource(); // get current corpus selection Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue(); if (service != null) { try { List<AnnisAttribute> atts = new LinkedList<>(); for (String corpus : corpusSelection) { atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations") .queryParam("fetchvalues", "true").queryParam("onlymostfrequentvalues", "false") .get(new GenericType<List<AnnisAttribute>>() { })); } for (AnnisAttribute a : atts) { if (a.getType() == AnnisAttribute.Type.node) { String aa = killNamespace(a.getName()); if (aa.equals(meta)) { result.addAll(a.getValueSet()); } } } } catch (ClientHandlerException ex) { log.error(null, ex); } catch (UniformInterfaceException ex) { log.error(null, ex); } } return result; } public String killNamespace(String qName) { String[] splitted = qName.split(":", 2); if (splitted.length > 1) { return splitted[1]; } else { return qName; } } public Set<String> getAvailableMetaNames() { Set<String> result = new TreeSet<>(); WebResource service = Helper.getAnnisWebResource(); // get current corpus selection Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue(); if (service != null) { try { List<AnnisAttribute> atts = new LinkedList<>(); for (String corpus : corpusSelection) { atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations") .get(new GenericType<List<AnnisAttribute>>() { })); } for (AnnisAttribute a : atts) { if (a.getType() == AnnisAttribute.Type.meta) { String aa = killNamespace(a.getName()); result.add(aa); } } } catch (ClientHandlerException ex) { log.error(null, ex); } catch (UniformInterfaceException ex) { log.error(null, ex); } } return result; } public Set<String> getAvailableMetaLevels(String meta) { Set<String> result = new TreeSet<>(); WebResource service = Helper.getAnnisWebResource(); // get current corpus selection Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue(); if (service != null) { try { List<AnnisAttribute> atts = new LinkedList<>(); for (String corpus : corpusSelection) { atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations") .queryParam("fetchvalues", "true").queryParam("onlymostfrequentvalues", "false") .get(new GenericType<List<AnnisAttribute>>() { })); } for (AnnisAttribute a : atts) { if (a.getType() == AnnisAttribute.Type.meta) { String aa = killNamespace(a.getName()); if (aa.equals(meta)) { result.addAll(a.getValueSet()); } } } } catch (ClientHandlerException ex) { log.error(null, ex); } catch (UniformInterfaceException ex) { log.error(null, ex); } } return result; } public String getFilterMechanism() { return filtering.getItemCaption(filtering.getValue()); } private void clear() //check whether it is necessary to do this in a method { language.removeAllComponents(); span.removeAllComponents(); meta.removeAllComponents(); toolbar.removeAllComponents(); mainLayout.removeComponent(language); mainLayout.removeComponent(span); mainLayout.removeComponent(meta); mainLayout.removeComponent(toolbar); vnodes.clear(); eboxes.clear(); mboxes.clear(); } private Collection<String> splitMultipleValueExpression(String expression) /* * only for complex regex expressions */ { ArrayList<String> values = new ArrayList<>(); String s = expression; while (s.length() > 0) { if (s.charAt(0) == '|') { s = s.substring(1); } if (s.charAt(0) != '(') { values = new ArrayList<>(); values.add(expression); return values; } else { int pc = 1; int i = 1; while (pc != 0) { char c = s.charAt(i); if (c == ')') { pc--; } else if (c == '(') { pc++; } i++; } values.add(unescapeSlQ(s.substring(1, i - 1))); //in respect to removal of parentheses s = s.substring(i); } } return values; } public ReducingStringComparator getRSC() { return rsc; } public void loadQuery() throws UnknownLevelException, EqualityConstraintException, MultipleAssignmentException, InvalidCharacterSequenceException, EmptyReferenceException /* * this method is called by btInverse * When the query has changed in the * textfield, the query represented by * the query builder is not equal to * the one delivered by the text field */ { /*get clean query from control panel text field*/ String tq = cp.getState().getAql().getValue().replace("\n", " ").replace("\r", ""); //TODO VALIDATE QUERY: (NOT SUFFICIENT YET) boolean valid = (!tq.equals("")); if (!(query.equals(tq)) & valid) { //PROBLEM: LINE BREAKS (simple without anything else) try { //the dot marks the end of the string - it is not read, it's just a place holder tq += "."; HashMap<Integer, Constraint> constraints = new HashMap<>(); ArrayList<Relation> pRelations = new ArrayList<>(); ArrayList<Relation> eRelations = new ArrayList<>(); Relation inclusion = null; Constraint conInclusion = null; ArrayList<Constraint> metaConstraints = new ArrayList<>(); //parse typed-in Query //Step 1: get indices of tq-chars, where constraints are separated (&) String tempCon = ""; int count = 1; int maxId = 0; boolean inclusionCheck = false; for (int i = 0; i < tq.length(); i++) { //improve this Algorithm (compare to constraint) char c = tq.charAt(i); if ((c != '&') & (i != tq.length() - 1)) { if (!((tempCon.length() == 0) & (c == ' '))) //avoids, that a constraint starts with a space { tempCon += c; } } else { while (tempCon.charAt(tempCon.length() - 1) == ' ') { tempCon = tempCon.substring(0, tempCon.length() - 1); } if (tempCon.startsWith("meta::")) { metaConstraints.add(new Constraint(tempCon)); } else if (tempCon.startsWith("#")) { Relation r = new Relation(tempCon); if (r.getType() == RelationType.EQUALITY) { eRelations.add(r); } else if (r.getType() == RelationType.PRECEDENCE) { pRelations.add(r); } else if ((r.getType() == RelationType.INCLUSION) & (!inclusionCheck)) { inclusion = r; if (constraints.containsKey(r.getFirst())) { conInclusion = constraints.get(r.getFirst()); constraints.remove(r.getFirst()); } inclusionCheck = true; } int newMax = (r.getFirst() > r.getSecond()) ? r.getFirst() : r.getSecond(); maxId = (maxId < newMax) ? newMax : maxId; } else { constraints.put(count++, new Constraint(tempCon)); } tempCon = ""; } } /*CHECK FOR EMPTY REFERENCE*/ /*IDEA: If the highest element-id is not empty, all lower ids can't be empty*/ /*one additional increment of count has to be taken into account*/ /*empty means, the element the id is refering to does not exist*/ if (maxId >= count) { throw new EmptyReferenceException(Integer.toString(maxId)); } /*CHECK FOR INVALID OR REDUNDANT MUTLIPLE VALUE ASSIGNMENT*/ for (Relation rel : eRelations) { Constraint con1 = constraints.get(rel.getFirst()); Constraint con2 = constraints.get(rel.getSecond()); if (con1.getLevel().equals(con2.getLevel())) { throw new MultipleAssignmentException(con1.toString() + " <-> " + con2.toString()); } } //create Vertical Nodes HashMap<Integer, VerticalNode> indexedVnodes = new HashMap<>(); VerticalNode vn = null; Collection<String> annonames = getAvailableAnnotationNames(); for (int i : constraints.keySet()) { Constraint con = constraints.get(i); if (!annonames.contains(con.getLevel())) { throw new UnknownLevelException(con.getLevel()); //is that a good idea? YES } if (!indexedVnodes.containsKey(i)) { vn = new VerticalNode(con.getLevel(), con.getValue(), this, con.isRegEx(), con.isNegative()); if (con.isRegEx()) { SearchBox sb = vn.getSearchBoxes().iterator().next(); /*CHECK FIRST IF WE REALLY HAVE A MULTIPLE VALUE EXPRESSION*/ Collection<String> mvalue = splitMultipleValueExpression(con.getValue()); if (mvalue.size() == 1) { sb.setValue(mvalue.iterator().next()); } else { sb.setValue(mvalue); } } indexedVnodes.put(i, vn); } for (Relation rel : eRelations) { if (rel.contains(i)) { int b = rel.whosMyFriend(i); if (!indexedVnodes.containsKey(b)) { indexedVnodes.put(b, null); Constraint bcon = constraints.get(b); SearchBox sb = new SearchBox(bcon.getLevel(), this, vn, bcon.isRegEx(), bcon.isNegative()); Collection<String> values = splitMultipleValueExpression(bcon.getValue()); if (values.size() > 1) { sb.setValue(values); } else { sb.setValue(bcon.getValue()); } vn.addSearchBox(sb); } } } } //clean query builder surface for (VerticalNode v : vnodes) { languagenodes.removeComponent(v); } vnodes.clear(); for (EdgeBox eb : eboxes) { languagenodes.removeComponent(eb); } eboxes.clear(); for (MetaBox mb : mboxes) { meta.removeComponent(mb); } mboxes.clear(); //remove SpanBox removeSpanBox(); VerticalNode first = null; int smP = (!pRelations.isEmpty()) ? pRelations.iterator().next().getFirst() : 0; int smE = (!eRelations.isEmpty()) ? eRelations.iterator().next().getFirst() : 0; if ((smP + smE) == 0) { if (!indexedVnodes.isEmpty()) { first = indexedVnodes.values().iterator().next(); } } else if ((smP != 0) & (smE != 0)) { first = indexedVnodes.get(Math.min(smE, smP)); } else { //one value is zero first = indexedVnodes.get(smE + smP); } if (first != null) { vnodes.add(first); languagenodes.addComponent(first); for (Relation rel : pRelations) { EdgeBox eb = new EdgeBox(this); eb.setValue(rel.getOperator()); eboxes.add(eb); VerticalNode v = indexedVnodes.get(rel.getSecond()); vnodes.add(v); languagenodes.addComponent(eb); languagenodes.addComponent(v); } } //build SpanBox if (inclusion != null) { addSpanBox(new SpanBox(conInclusion.getLevel(), this, conInclusion.isRegEx())); spbox.setValue(conInclusion.getValue()); } //build MetaBoxes for (Constraint mc : metaConstraints) { if (mc.isRegEx()) { Collection<String> values = splitMultipleValueExpression( unescape(unescapeSlQ(mc.getValue()))); MetaBox mb = new MetaBox(mc.getLevel(), this); mb.setValue(values); mboxes.add(mb); meta.addComponent(mb); } else { MetaBox mb = new MetaBox(mc.getLevel(), this); //for a particular reason (unknown) setValue() with a String parameter //is not accepted by OptionGroup Collection<String> values = new TreeSet<>(); values.add(unescapeSlQ(mc.getValue())); mb.setValue(values); mboxes.add(mb); meta.addComponent(mb); } } query = tq.substring(0, tq.length() - 1); } catch (EmptyReferenceException e) { Notification.show(e.getMessage()); } catch (EqualityConstraintException e) { Notification.show(e.getMessage()); } catch (InvalidCharacterSequenceException e) { Notification.show(e.getMessage()); } catch (MultipleAssignmentException e) { Notification.show(e.getMessage()); } catch (UnknownLevelException e) { Notification.show(e.getMessage()); } } } private class Constraint { private String level; private String value; private boolean regEx; private boolean negative; public Constraint(String s) throws InvalidCharacterSequenceException { int e = 0; if (s.contains("=")) { while (s.charAt(e) != '=') { e++; } String l; if (s.charAt(e - 1) == '!') { l = s.substring(0, e - 1).replace(" ", ""); negative = true; } else { l = s.substring(0, e).replace(" ", "").replace("meta::", ""); negative = false; } String v = s.substring(e + 1); while (v.startsWith(" ")) { v = v.substring(1); } if (v.startsWith("\"")) { regEx = false; } else { regEx = true; } //remove " or / : v = v.substring(1, v.length() - 1); level = l; value = v; } else if (((s.charAt(0) == '\"') && (s.charAt(s.length() - 1) == '\"')) || ((s.charAt(0) == '/') && (s.charAt(s.length() - 1) == '/'))) { level = "tok"; value = s.substring(1, s.length() - 1); } else if ((s.contains("\"")) || (s.contains("/"))) { throw new InvalidCharacterSequenceException(s); } else { level = s; value = ""; } } public String getLevel() { return level; } public String getValue() { return value; } public boolean isRegEx() { return regEx; } public boolean isNegative() { return negative; } @Override public String toString() { String op = (negative) ? "!=" : "="; String val = (regEx) ? "/" + value + "/" : "\"" + value + "\""; return level + op + val; } } private class Relation /* * Problems: * if an operator is used, which is not in the EdgeBoxe's list * the programm will crash. Right now. I'm gonna fix this. * */ { //operands without '#' private int o1, o2; private String operator; private RelationType type; public Relation(String in) throws EqualityConstraintException { StringBuilder op = new StringBuilder(); StringBuilder o1str = new StringBuilder(); StringBuilder o2str = new StringBuilder(); int i = 1; char c = in.charAt(1); in = in.replace(" ", ""); while ((c != '.') & (c != '>') & (c != '_') & (c != '#') & (c != '-') & (c != '$') & (c != '=')) { o1str.append(c); i++; c = in.charAt(i); } while (c != '#') { op.append(c); i++; c = in.charAt(i); } i++; while ((i < in.length())) { c = in.charAt(i); o2str.append(c); i++; } operator = op.toString(); o1 = Integer.parseInt(o1str.toString()); o2 = Integer.parseInt(o2str.toString()); if (operator.startsWith(".")) { type = RelationType.PRECEDENCE; } else if ((operator.equals("=")) || (operator.equals("_=_"))) { type = RelationType.EQUALITY; if (o1 > o2) { int tmp = o1; o1 = o2; o2 = tmp; } else if (o1 == o2) { throw new EqualityConstraintException(in); } } else if (operator.equals("_i_")) { type = RelationType.INCLUSION; } } public RelationType getType() { return type; } public int getFirst() { return o1; } public int getSecond() { return o2; } public String getOperator() { return operator; } public boolean contains(int a) { return ((o1 == a) | (o2 == a)); } public int whosMyFriend(int a) { if (a == o1) { return o2; } if (a == o2) { return o1; } return 0; } } private enum RelationType { PRECEDENCE, DOMINANCE, INCLUSION, EQUALITY } private abstract class LoadQueryException extends Exception { protected String ERROR_MESSAGE; protected String critical; public LoadQueryException(String s) { critical = s; } @Override public String getMessage() { return ERROR_MESSAGE + critical; } } private class UnknownLevelException extends LoadQueryException { public UnknownLevelException(String s) { super(s); ERROR_MESSAGE = "Unknown annotation level: "; } } private class MultipleAssignmentException extends LoadQueryException { public MultipleAssignmentException(String s) { super(s); ERROR_MESSAGE = "Invalid or redundant assignment of multiple values:\n\n"; } } private class EqualityConstraintException extends LoadQueryException { public EqualityConstraintException(String s) { super(s); ERROR_MESSAGE = "Invalid use of equality operator: "; } } private class InvalidCharacterSequenceException extends LoadQueryException { public InvalidCharacterSequenceException(String s) { super(s); ERROR_MESSAGE = "Invalid character sequence: \n\n"; } } private class EmptyReferenceException extends LoadQueryException { public EmptyReferenceException(String s) { super(s); ERROR_MESSAGE = "Element not found. Empty reference: #"; } } }