/* * Copyright 2011 - AndroidQuery.com (tinyeeliu@gmail.com) * * 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 com.smartandroid.sa.aq; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xmlpull.v1.XmlSerializer; import android.util.Xml; /** * Specialized class for simple and easy XML parsing. Designed to be used in basic Android API 4+ * runtime without any dependency. There's no support to modify the dom object. * * The toString method return a string that represents the xml content. * WARNING: The toString methods are for debugging only and do not guarantee a proper XML transformation. * */ public class XmlDom { private Element root; /** * Gets the element that this node represent. * * @return the element * * @see testGetElement */ public Element getElement(){ return root; } /** * Instantiates a new xml dom. * * @param element the element */ public XmlDom(Element element){ this.root = element; } /** * Instantiates a new xml dom. * * @param str Raw XML * @throws SAXException the SAX exception */ public XmlDom(String str) throws SAXException{ this(str.getBytes()); } /** * Instantiates a new xml dom. * * @param data Raw XML * @throws SAXException the SAX exception */ public XmlDom(byte[] data) throws SAXException{ this(new ByteArrayInputStream(data)); } /** * Instantiates a new xml dom. * * @param is Raw XML. * @throws SAXException the SAX exception */ public XmlDom(InputStream is) throws SAXException{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); Document doc = builder.parse(is); this.root = (Element) doc.getDocumentElement(); }catch(ParserConfigurationException e) { }catch(IOException e){ throw new SAXException(e); } } /** * Return a node that represents the first matched tag. * * A dummy node is returned if none found. * * @param tag tag name * @return the xml dom * * @see testTag */ public XmlDom tag(String tag){ NodeList nl = root.getElementsByTagName(tag); XmlDom result = null; if(nl != null && nl.getLength() > 0){ result = new XmlDom((Element) nl.item(0)); } return result; } /** * Return a node that represents the first matched tag. * * If value == null, node that has the attr are considered a match. * * A dummy node is returned if none found. * * @param tag tag name * @param attr attr name to match * @param value attr value to match * @return the xml dom * * @see testTag2 */ public XmlDom tag(String tag, String attr, String value){ List<XmlDom> tags = tags(tag, attr, value); if(tags.size() == 0){ return null; }else{ return tags.get(0); } } /** * Return a list of nodes that represents the matched tags. * * @param tag tag name * @return the list of xml dom * * @see testTags */ public List<XmlDom> tags(String tag){ return tags(tag, null, null); } /** * Return the first child node that represent the matched tag. * A dummy node is returned if none found. * * @param tag tag name * @return the list of xml dom * * @see testChild */ public XmlDom child(String tag){ return child(tag, null, null); } /** * Return the first child node that represent the matched tag that has attribute attr=value. * A dummy node is returned if none found. * * @param tag tag name * @param attr attr name to match * @param value attr value to match * @return the list of xml dom * * @see testChild2 */ public XmlDom child(String tag, String attr, String value){ List<XmlDom> c = children(tag, attr, value); if(c.size() == 0) return null; return c.get(0); } /** * Return a list of child nodes that represents the matched tags. * * @param tag tag name * @return the list of xml dom * * @see testChildren */ public List<XmlDom> children(String tag){ return children(tag, null, null); } /** * Return a list of child nodes that represents the matched tags. * * @param tag tag name * @param attr attr name to match * @param value attr value to match * @return the list of xml dom * * @see testChildren2 */ public List<XmlDom> children(String tag, String attr, String value){ return convert(root.getChildNodes(), tag, attr, value); } /** * Return a list of nodes that represents the matched tags that has attribute attr=value. * If attr == null, any tag with specified name matches. * If value == null, any nodes that has the attr matches. * * @param tag tag name * @param attr attr name to match * @param value attr value to match * @return the list of xml dom * * @see testTags2 */ public List<XmlDom> tags(String tag, String attr, String value){ NodeList nl = root.getElementsByTagName(tag); return convert(nl, null, attr, value); } //convert to list and filter to nodes that has attr=value private static List<XmlDom> convert(NodeList nl, String tag, String attr, String value){ List<XmlDom> result = new ArrayList<XmlDom>(); for(int i = 0; i < nl.getLength(); i++){ XmlDom xml = convert(nl.item(i), tag, attr, value); if(xml != null) result.add(xml); } return result; } private static XmlDom convert(Node node, String tag, String attr, String value){ if(node.getNodeType() != Node.ELEMENT_NODE){ return null; } Element e = (Element) node; XmlDom result = null; if(tag == null || tag.equals(e.getTagName())){ if(attr == null || e.hasAttribute(attr)){ if(value == null || value.equals(e.getAttribute(attr))){ result = new XmlDom(e); } } } return result; } /** * Return the text content of the first matched tag. * Short cut for "xml.child(tag).text()" * * Return null if there's no matched tag. * * @param tag tag name * @return text * * @see testText2 */ public String text(String tag){ XmlDom dom = child(tag); if(dom == null) return null; return dom.text(); } /** * Return the value of the attribute of current node. * * @param name attribute name * @return value * * @see testAttr */ public String attr(String name){ String result = root.getAttribute(name); return result; } /** * Return the raw xml of this node. * * WARNING: This method is for debugging only. Does not guarantee a proper XML transformation. * * @return raw xml * * @see testToString */ public String toString(){ return toString(0); } /** * Return the raw xml of this node. * * WARNING: This method is for debugging only. Does not guarantee a proper XML transformation. * * @param intentSpaces number of white spaces to intent * @return raw xml * * @see testToString2 */ public String toString(int intentSpaces){ return serialize(root, intentSpaces); } private String serialize(Element e, int intent){ try{ XmlSerializer s = Xml.newSerializer(); StringWriter sw = new StringWriter(); s.setOutput(sw); s.startDocument("utf-8", null); String spaces = null; if(intent > 0){ char[] chars = new char[intent]; Arrays.fill(chars, ' '); spaces = new String(chars); } serialize(root, s, 0, spaces); s.endDocument(); return sw.toString(); }catch(Exception ex){ ex.printStackTrace(); } return null; } private void writeSpace(XmlSerializer s, int depth, String spaces) throws Exception{ if(spaces != null){ s.text("\n"); for(int i = 0; i < depth; i++){ s.text(spaces); } } } /** * Return the text content of the current node. Returns empty string if there's no text or cdata child elements. * * @return text * * @see testText */ public String text(){ NodeList list = root.getChildNodes(); if(list.getLength() == 1) return list.item(0).getNodeValue(); StringBuilder sb = new StringBuilder(); for(int i = 0; i < list.getLength(); i++){ sb.append(text(list.item(i))); } return sb.toString(); } private String text(Node n){ String text = null; switch(n.getNodeType()){ case Node.TEXT_NODE: text = n.getNodeValue(); if(text != null) text = text.trim(); break; case Node.CDATA_SECTION_NODE: text = n.getNodeValue(); break; default: //AQUtility.debug("unknown", n); } if(text == null) text = ""; return text; } private void serialize(Element e, XmlSerializer s, int depth, String spaces) throws Exception{ String name = e.getTagName(); writeSpace(s, depth, spaces); s.startTag("", name); if(e.hasAttributes()){ NamedNodeMap nm = e.getAttributes(); for(int i = 0; i < nm.getLength(); i++){ Attr attr = (Attr) nm.item(i); s.attribute("", attr.getName(), attr.getValue()); } } if(e.hasChildNodes()){ NodeList nl = e.getChildNodes(); int elements = 0; for(int i = 0; i < nl.getLength(); i++){ Node n = nl.item(i); short type = n.getNodeType(); switch(type){ case Node.ELEMENT_NODE: serialize((Element) n, s, depth + 1, spaces); elements++; break; case Node.TEXT_NODE: s.text(text(n)); break; case Node.CDATA_SECTION_NODE: s.cdsect(text(n)); break; } } if(elements > 0){ writeSpace(s, depth, spaces); } } s.endTag("", name); } }