//Dstl (c) Crown Copyright 2017 package uk.gov.dstl.baleen.uima.utils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.apache.uima.jcas.tcas.Annotation; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import uk.gov.dstl.baleen.types.structure.Structure; import uk.gov.dstl.baleen.uima.utils.select.AbstractNode; import uk.gov.dstl.baleen.uima.utils.select.Node; /** * A node in the structure hierarchy of the document * <p> * All nodes except the root node contain a structure annotation and gives access to the parent and * child structure nodes. * */ public class AnnotationNode<T extends Annotation> extends AbstractNode<T> { private static final Set<String> nonAttributes = ImmutableSet.of("getElementClass", "getElementId", "getTypeIndexID"); /** lazily created attribute map */ private Map<String, String> attributes; /** * Constructor for the Structure Node. * * @param parent the parent node * @param annotation the structure */ protected AnnotationNode(AnnotationNode<T> parent, T annotation) { super(parent, annotation); } /** * Add child node, to be used during construction of the hierarchy * * @param node the child node */ protected void addChild(AnnotationNode<T> node) { super.addChild(node); } @Override public String getTypeName() { if (getItem() == null) { return "Root"; } else { return getItem().getType().getShortName(); } } @Override public String text() { if (getItem() == null) { return ""; } else { return getItem().getCoveredText(); } } @Override public Map<String, String> attributes() { if (attributes != null) { return attributes; } attributes = new HashMap<>(); T annotation = getItem(); if (annotation != null) { if (annotation instanceof Structure) { addAttribute(attributes, "class", ((Structure) annotation).getElementClass()); addAttribute(attributes, "id", ((Structure) annotation).getElementId()); } for (Method method : annotation.getClass().getDeclaredMethods()) { String name = method.getName(); if (name.startsWith("get") && method.getParameterTypes().length == 0 && !nonAttributes.contains(name)) { String key = name.substring(3).toLowerCase(); try { addAttribute(attributes, key, method.invoke(annotation)); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } } } } return attributes; } /** * Add attribute to the map if not null * * @param attributes the attributes map * @param key the attribute key * @param value the value */ private void addAttribute(Map<String, String> attributes, String key, Object value) { if (value != null) { attributes.put(key, value.toString()); } } @Override public String ownText() { StringBuilder sb = new StringBuilder(text()); List<Node<T>> reversed = ImmutableList.copyOf(getChildren()).reverse(); for (Node<T> s : reversed) { sb.replace(getBegin(s) - getBegin(this), getEnd(s) - getBegin(this), ""); } return sb.toString(); } @Override public List<String> getClasses() { T annotation = getItem(); if (annotation instanceof Structure) { String classes = ((Structure) annotation).getElementClass(); if (StringUtils.isNotBlank(classes)) { return Arrays.asList(classes.split("\\s")).stream().map(String::toLowerCase) .collect(Collectors.toList()); } } return ImmutableList.of(); } @Override public String id() { T annotation = getItem(); if (annotation instanceof Structure) { String id = ((Structure) annotation).getElementId(); if (id != null) { return id; } } return ""; } /** * Get the begin offset of the contained structure node (or 0). * * @return the begin */ public static int getBegin(Node<? extends Annotation> node) { return node.getItem() == null ? 0 : node.getItem().getBegin(); } /** * Get the end offset of the contained structure node (or int max). * * @return the end */ public static int getEnd(Node<? extends Annotation> node) { return node.getItem() == null ? Integer.MAX_VALUE : node.getItem().getEnd(); } }