package nl.knaw.huygens.alexandria.api.model.text.view; /* * #%L * alexandria-api * ======= * Copyright (C) 2015 - 2017 Huygens ING (KNAW) * ======= * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import static java.util.stream.Collectors.toList; import static nl.knaw.huygens.alexandria.api.model.text.view.ElementView.AttributeMode.hideAll; import static nl.knaw.huygens.alexandria.api.model.text.view.ElementView.AttributeMode.hideOnly; import static nl.knaw.huygens.alexandria.api.model.text.view.ElementView.AttributeMode.showAll; import static nl.knaw.huygens.alexandria.api.model.text.view.ElementView.AttributeMode.showOnly; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import nl.knaw.huygens.alexandria.api.model.text.view.ElementView.AttributeFunction; import nl.knaw.huygens.alexandria.api.model.text.view.ElementView.AttributeMode; import nl.knaw.huygens.alexandria.util.XMLUtil; public class TextViewDefinitionParser { private final List<String> errors = new ArrayList<>(); static TextView textView = new TextView(); public TextViewDefinitionParser(final TextViewDefinition d) { textView = new TextView(); errors.clear(); parse(d); } public Optional<TextView> getTextView() { return isValid() ? Optional.of(textView) : Optional.empty(); } public boolean isValid() { return errors.isEmpty(); } public List<String> getErrors() { return errors; } private void parse(final TextViewDefinition textViewDefinition) { textView.setDescription(textViewDefinition.getDescription()); List<List<String>> parseAnnotationLayers = parseAnnotationLayers(textViewDefinition.getAnnotationLayers(), textViewDefinition.getAnnotationLayerDepthOrder()); textView.setOrderedLayerTags(parseAnnotationLayers); Map<String, ElementViewDefinition> elementViewDefinitions = textViewDefinition.getElementViewDefinitions(); elementViewDefinitions.putIfAbsent(TextViewDefinition.DEFAULT_ATTRIBUTENAME, ElementViewDefinition.DEFAULT); ElementViewDefinition defaultElementViewDefinition = elementViewDefinitions.get(TextViewDefinition.DEFAULT_ATTRIBUTENAME); for (final Entry<String, ElementViewDefinition> entry : elementViewDefinitions.entrySet()) { final String elementName = entry.getKey(); if (!TextViewDefinition.DEFAULT_ATTRIBUTENAME.equals(elementName)) { errors.addAll(XMLUtil.validateElementName(elementName)); } final ElementView elementView = parseElementViewDefinition(elementName, entry.getValue()); if (elementView.getElementMode() == null) { elementView.setElementMode(defaultElementViewDefinition.getElementMode()// .orElse(ElementViewDefinition.DEFAULT.getElementMode().get())); } if (elementView.getAttributeMode() == null) { String modeString = defaultElementViewDefinition.getAttributeMode()// .orElse(ElementViewDefinition.DEFAULT.getAttributeMode().get()); elementView.setAttributeMode(AttributeMode.valueOf(modeString)); } textView.putElementView(elementName, elementView); } } private List<List<String>> parseAnnotationLayers(Map<String, List<String>> annotationLayers, List<String> annotationLayerDepthOrder) { Set<String> definedLayers = annotationLayers.keySet(); annotationLayerDepthOrder.forEach(layerName -> { if (!definedLayers.contains(layerName)) { errors.add("annotationLayerDepthOrder: " + layerName + " not defined in annotationLayers"); } }); List<List<String>> list = annotationLayerDepthOrder.stream()// .map(annotationLayers::get)// .collect(toList()); return list; } private ElementView parseElementViewDefinition(final String elementName, final ElementViewDefinition evd) { final ElementView elementView = new ElementView(); evd.getElementMode().ifPresent(elementView::setElementMode); parseAttributeMode(elementName, evd.getAttributeMode(), elementView); parseWhen(elementName, evd.getWhen(), elementView); return elementView; } static final Pattern ATTRIBUTEMODE_SHOWONLY = Pattern.compile(showOnly.name() + "(.*)"); static final Pattern ATTRIBUTEMODE_HIDEONLY = Pattern.compile(hideOnly.name() + "(.*)"); private void parseAttributeMode(final String elementName, final Optional<String> attributeMode, final ElementView elementView) { attributeMode.ifPresent(mode -> { final String prefix = MessageFormat.format("{0}: \"{1}\" ", elementName, mode); if (showAll.name().equals(mode)) { elementView.setAttributeMode(showAll, new ArrayList<>()); return; } final Matcher matcher2 = ATTRIBUTEMODE_SHOWONLY.matcher(mode); if (matcher2.matches()) { List<String> parameters = parseParameters(prefix, matcher2.group(1)); elementView.setAttributeMode(showOnly, parameters); return; } if (hideAll.name().equals(mode)) { elementView.setAttributeMode(hideAll, new ArrayList<>()); return; } final Matcher matcher4 = ATTRIBUTEMODE_HIDEONLY.matcher(mode); if (matcher4.matches()) { List<String> parameters = parseParameters(prefix, matcher4.group(1)); elementView.setAttributeMode(hideOnly, parameters); return; } errors.add(prefix + "is not a valid attributeMode. Valid attributeMode values are: \"showAll\", \"showOnly attribute...\", \"hideAll\", \"hideOnly attribute...\"."); }); } private List<String> parseParameters(final String prefix, final String group) { final List<String> parameters = Splitter.on(" ").trimResults().omitEmptyStrings().splitToList(group); if (parameters.isEmpty()) { errors.add(prefix + "needs one or more attribute names."); } return parameters; } static final Pattern WHEN_PATTERN = Pattern.compile("attribute\\((\\w+|\\{\\w+\\})\\)\\.(\\w+)\\((.*)\\)"); private void parseWhen(final String elementName, final Optional<String> optionalWhen, final ElementView elementView) { if (optionalWhen.isPresent()) { final String when = optionalWhen.get(); final String prefix = MessageFormat.format("{0}: \"{1}\" ", elementName, when); Matcher matcher = WHEN_PATTERN.matcher(when); if (matcher.matches()) { String attribute = matcher.group(1); String function = matcher.group(2); try { AttributeFunction attributeFunction = AttributeFunction.valueOf(AttributeFunction.class, function); String parameterString = matcher.group(3); List<String> parameters = Splitter.on(",")// .trimResults(CharMatcher.is('\''))// .splitToList(parameterString); elementView.setPreCondition(attribute, attributeFunction, parameters); } catch (IllegalArgumentException e) { addInvalidWhenError(prefix); } return; } addInvalidWhenError(prefix); } } private void addInvalidWhenError(final String prefix) { errors.add(prefix + "is not a valid condition. Valid 'when' values are: \"attribute(a).is('value')\", \"attribute(a).isNot('value')\", \"attribute(a).firstOf('value0','value1',...)\"."); } }