package edu.stanford.nlp.semparse.open.model.candidate; import java.util.*; import edu.stanford.nlp.semparse.open.model.tree.KNode; import edu.stanford.nlp.semparse.open.model.tree.KNodeUtils; import fig.basic.LogInfo; /** * Utilities that deal with XPath (list of PathEntry) */ public class PathUtils { public static String getXPathString(List<PathEntry> path) { StringBuilder sb = new StringBuilder(); for (PathEntry entry : path) { sb.append("/").append(entry); } return sb.toString(); } public static String getXPathSuffixString(List<PathEntry> path, int amount) { StringBuilder sb = new StringBuilder(); int startIndex = Math.max(0, path.size() - amount); for (PathEntry entry : path.subList(startIndex, path.size())) { sb.append("/").append(entry.toString()); } return sb.toString(); } public static String getXPathSuffixStringNoIndex(List<PathEntry> path, int amount) { StringBuilder sb = new StringBuilder(); int startIndex = Math.max(0, path.size() - amount); for (PathEntry entry : path.subList(startIndex, path.size())) { sb.append("/").append(entry.tag); } return sb.toString(); } public static List<PathEntry> getXPathSuffix(List<PathEntry> path, int amount) { List<PathEntry> suffix = new ArrayList<>(); int startIndex = Math.max(0, path.size() - amount); for (PathEntry entry : path.subList(startIndex, path.size())) { suffix.add(entry); } return suffix; } public static List<PathEntry> getXPathSuffixNoIndex(List<PathEntry> path, int amount) { List<PathEntry> suffix = new ArrayList<>(); int startIndex = Math.max(0, path.size() - amount); for (PathEntry entry : path.subList(startIndex, path.size())) { suffix.add(entry.getNoIndexVersion()); } return suffix; } /** * Execute XPath on the currentNode and add the matched nodes to the answer collection. * Only add nodes with short text (i.e., fullText != null and fullText != "") */ public static void executePath(List<PathEntry> path, KNode currentNode, Collection<KNode> answer) { if (!path.get(0).matchTag(currentNode)) LogInfo.fails("XPath mismatch (node %s != xpath %s)", currentNode.value, path.get(0).tag); if (path.size() == 1) { if (currentNode.fullText != null && !currentNode.fullText.isEmpty()) answer.add(currentNode); return; } // Go to the next element of the path List<PathEntry> descendants = path.subList(1, path.size()); PathEntry nextPathEntry = descendants.get(0); if (nextPathEntry instanceof PathEntryWithRange) { PathEntryWithRange nextPathEntryWithRange = (PathEntryWithRange) nextPathEntry; List<KNode> children = currentNode.getChildrenOfTag(nextPathEntry.tag); int start = nextPathEntryWithRange.indexStart; int end = children.size() - nextPathEntryWithRange.indexEnd; if (start >= end) return; for (KNode child : children.subList(start, end)) { executePath(descendants, child, answer); } } else if (nextPathEntry.isIndexed()) { KNode child = currentNode.getChildrenOfTag(nextPathEntry.tag, nextPathEntry.index); if (child != null) executePath(descendants, child, answer); } else { for (KNode child : currentNode.getChildrenOfTag(nextPathEntry.tag)) executePath(descendants, child, answer); } } /** * Return the prefix of the given XPath where the last entry of the prefix refers to the given node. */ public static List<PathEntry> pathPrefixAtNode(List<PathEntry> path, KNode root, KNode node) { if (!KNodeUtils.isDescendantOf(node, root)) { LogInfo.fails("%s not a descendant of %s", node, root); } return path.subList(0, node.depth - root.depth); } /** * Return the suffix of the given XPath where the first entry of the suffix refers to the given node. */ public static List<PathEntry> pathSuffixAtNode(List<PathEntry> path, KNode root, KNode node) { if (!KNodeUtils.isDescendantOf(node, root)) { LogInfo.fails("%s not a descendant of %s", node, root); } return path.subList(node.depth - root.depth, path.size()); } }