/* ************************************************************************ # # DivConq # # http://divconq.com/ # # Copyright: # Copyright 2014 eTimeline, LLC. All rights reserved. # # License: # See the license.txt file in the project's top-level directory for details. # # Authors: # * Andy White # ************************************************************************ */ package divconq.xml; import divconq.lang.Memory; /** * Super class to support all xml classes (XElement and XTex) * * @author Andy * */ public abstract class XNode { /** * Returns formatted or unformatted XML source. * * @param formatted * whether to return formatted XML source. If true, the source is * pretty-printed with new lines and indentation. If false, the XML * string is returned as one lone, unformatted line. * @return a String containing the XML source */ public String toString(boolean formatted) { StringBuffer sb = new StringBuffer(); return toString(sb, formatted, 0).toString(); } /** * Internal method used recursively to format XML with appropriate * indentation. * * @param sb * destination for xml (character) output * @param formatted * whether to return formatted XML source. If true, the source is * pretty-printed with new lines and indentation. If false, the XML * string is returned as one long, unformatted line. * @param level * the indentation level used to write leading spaces * * @return a String containing the XML source */ protected abstract StringBuffer toString(StringBuffer sb, boolean formatted, int level); /** * quotes a string according to XML rules. When attributes and text elements * are written out special characters have to be quoted. * * @param string * the string to process * @return the string with quoted special characters */ public static String quote(String string) { if (string == null) return null; StringBuffer sb = new StringBuffer(); for (int i = 0; i < string.length(); i++) { char ch = string.charAt(i); switch (ch) { case '&': sb.append("&"); break; case '<': sb.append("<"); break; case '>': sb.append(">"); break; case '"': sb.append("""); break; case '\'': sb.append("'"); break; default: sb.append(ch); } } return sb.toString(); } public static String quote(char ch) { switch (ch) { case '&': return "&"; case '<': return "<"; case '>': return ">"; case '"': return """; case '\'': return "'"; default: return ch + ""; } } /** * Returns formatted or unformatted XML source. * * @param formatted * whether to return formatted XML source. If true, the source is * pretty-printed with new lines and indentation. If false, the XML * string is returned as one lone, unformatted line. * @return Memory containing the XML source (utf-8) */ public Memory toMemory(boolean formatted) { Memory m = new Memory(); this.toMemory(m, formatted, 0); return m; } abstract protected void toMemory(Memory sb, boolean formatted, int level); abstract public XNode deepCopy(); /** * quotes a string according to XML rules. When attributes and text elements * are written out special characters have to be quoted. * * @param string * the string to process * @return the string with quoted special characters */ public static String unquote(String string) { if (string == null) return null; StringBuffer sb = new StringBuffer(); boolean inQuote = false; StringBuffer quoteBuf = new StringBuffer(); for (int i = 0; i < string.length(); i++) { char ch = string.charAt(i); if (inQuote) { if (ch == ';') { String quote = quoteBuf.toString(); if (quote.equals("lt")) sb.append('<'); else if (quote.equals("gt")) sb.append('>'); else if (quote.equals("amp")) sb.append('&'); else if (quote.equals("quot")) sb.append('"'); else if (quote.equals("apos")) sb.append('\''); else if (quote.startsWith("#x")) sb.append((char)Integer.parseInt(quote.substring(2), 16)); else if (quote.startsWith("#")) sb.append((char)Integer.parseInt(quote.substring(1))); else sb.append(quoteBuf); inQuote = false; quoteBuf.setLength(0); } else { quoteBuf.append(ch); } } else { if (ch == '&') inQuote = true; else sb.append(ch); } } if (inQuote) sb.append(quoteBuf); return sb.toString(); } }