package com.braintreegateway.util; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.StringReader; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class SimpleNodeWrapper extends NodeWrapper { private static SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); private String name; private Map<String, String> attributes = new HashMap<String, String>(); private List<Object> content = new LinkedList<Object>(); private SimpleNodeWrapper(String name) { this.name = name; } public static SimpleNodeWrapper parse(String xml) { try { InputSource source = new InputSource(new StringReader(xml)); SAXParser parser = saxParserFactory.newSAXParser(); MapNodeHandler handler = new MapNodeHandler(); parser.parse(source, handler); return handler.root; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage(), e); } } @Override public List<NodeWrapper> findAll(String expression) { String[] paths = expression.split("/"); LinkedList<String> tokens = new LinkedList<String>(Arrays.asList(paths)); List<NodeWrapper> nodes = new LinkedList<NodeWrapper>(); findAll(tokens, nodes); return nodes; } private void findAll(LinkedList<String> tokens, List<NodeWrapper> nodes) { if (tokens.isEmpty()) nodes.add(this); else { String first = tokens.getFirst(); if (".".equals(first)) findAll(restOf(tokens), nodes); for (SimpleNodeWrapper node : childNodes()) { if ("*".equals(first) || first.equals(node.name)) node.findAll(restOf(tokens), nodes); } } } private SimpleNodeWrapper find(LinkedList<String> tokens) { if (tokens.isEmpty()) return this; else { String first = tokens.getFirst(); if (".".equals(first)) return find(restOf(tokens)); for (SimpleNodeWrapper node : childNodes()) { if ("*".equals(first) || first.equals(node.name)) return node.find(restOf(tokens)); } return null; } } private SimpleNodeWrapper find(String expression) { String[] paths = expression.split("/"); LinkedList<String> tokens = new LinkedList<String>(Arrays.asList(paths)); return find(tokens); } private LinkedList<String> restOf(LinkedList<String> tokens) { LinkedList<String> newTokens = new LinkedList<String>(tokens); newTokens.removeFirst(); return newTokens; } @Override public NodeWrapper findFirst(String expression) { return find(expression); } @Override public String findString(String expression) { SimpleNodeWrapper node = find(expression); if (node == null) return null; else return node.stringValue(); } @Override public boolean isBlank() { return "true".equals(attributes.get("nil")); } private String stringValue() { if (content.size() == 1 && content.get(0) == null) return null; StringBuilder value = new StringBuilder(); for (Object o : content) { value.append(o.toString()); } return value.toString().trim(); } @Override public String getElementName() { return name; } private List<SimpleNodeWrapper> childNodes() { List<SimpleNodeWrapper> nodes = new LinkedList<SimpleNodeWrapper>(); for (Object o : content) { if (o instanceof SimpleNodeWrapper) nodes.add((SimpleNodeWrapper) o); } return nodes; } @Override public Map<String, String> getFormParameters() { Map<String, String> params = new HashMap<String, String>(); for (SimpleNodeWrapper node : childNodes()) { node.buildParams("", params); } return params; } private void buildParams(String prefix, Map<String, String> params) { List<SimpleNodeWrapper> childNodes = childNodes(); String newPrefix = "".equals(prefix) ? StringUtils.underscore(name) : prefix + "[" + StringUtils.underscore(name) + "]"; if (childNodes.isEmpty()) params.put(newPrefix, stringValue()); else { for (SimpleNodeWrapper childNode : childNodes) childNode.buildParams(newPrefix, params); } } @Override public String toString() { return "<" + name + (attributes.isEmpty() ? "" : " attributes=" + StringUtils.toString(attributes)) + " content=" + StringUtils.toString(content) + ">"; } private static class MapNodeHandler extends DefaultHandler { private static Pattern NON_WHITE_SPACE = Pattern.compile("\\S"); private Stack<SimpleNodeWrapper> stack = new Stack<SimpleNodeWrapper>(); public SimpleNodeWrapper root; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { SimpleNodeWrapper node = new SimpleNodeWrapper(qName); for (int i = 0; i < attributes.getLength(); i++) node.attributes.put(attributes.getQName(i), attributes.getValue(i)); if ("true".equals(node.attributes.get("nil"))) node.content.add(null); if (!stack.isEmpty()) stack.peek().content.add(node); stack.push(node); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { SimpleNodeWrapper pop = stack.pop(); if (stack.isEmpty()) root = pop; } @Override public void characters(char[] chars, int start, int length) throws SAXException { String value = new String(chars, start, length); Matcher matcher = NON_WHITE_SPACE.matcher(value); if (value.length() > 0 && matcher.find()) { stack.peek().content.add(value); } } } }