changeset 1468:35f3bfd4f51d

xml
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 14 Apr 2020 08:44:33 -0600
parents 509d49c493c0
children 21f5edab1fbf
files src/goodjava/xml/XmlElement.java src/goodjava/xml/XmlParser.java src/luan/modules/parsers/Xml.java
diffstat 3 files changed, 126 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/src/goodjava/xml/XmlElement.java	Mon Apr 13 22:16:59 2020 -0600
+++ b/src/goodjava/xml/XmlElement.java	Tue Apr 14 08:44:33 2020 -0600
@@ -8,13 +8,25 @@
 	public final Map<String,String> attributes;
 	public final Object content;
 
+	public XmlElement(String name,Map<String,String> attributes) {
+		this.name = name;
+		this.attributes = attributes;
+		this.content = null;
+	}
+
 	public XmlElement(String name,Map<String,String> attributes,String content) {
+		if( content == null )
+			throw new IllegalArgumentException("content can't be null");
 		this.name = name;
 		this.attributes = attributes;
 		this.content = content;
 	}
 
 	public XmlElement(String name,Map<String,String> attributes,XmlElement[] content) {
+		if( content == null )
+			throw new IllegalArgumentException("content can't be null");
+		if( content.length == 0 )
+			throw new IllegalArgumentException("content can't be empty");
 		this.name = name;
 		this.attributes = attributes;
 		this.content = content;
@@ -34,21 +46,29 @@
 			sb.append( ' ' );
 			sb.append( attribute.getKey() );
 			sb.append( "=\"" );
-			sb.append( attribute.getValue() );
+			sb.append( encode(attribute.getValue()) );
 			sb.append( '"' );
 		}
-		sb.append( '>' );
-		if( content instanceof String ) {
+		if( content == null ) {
+			sb.append( "/>\n" );
+		} else if( content instanceof String ) {
+			sb.append( '>' );
 			String s = (String)content;
-			sb.append(s);
+			sb.append( encode(s) );
+			closeTag(sb,name);
 		} else {
+			sb.append( '>' );
 			XmlElement[] elements = (XmlElement[])content;
 			sb.append( '\n' );
 			for( XmlElement element : elements ) {
 				element.toString(sb,indented+1);
 			}
 			indent(sb,indented);
+			closeTag(sb,name);
 		}
+	}
+
+	private static void closeTag(StringBuilder sb,String name) {
 		sb.append( "</" );
 		sb.append( name );
 		sb.append( ">\n" );
@@ -60,4 +80,29 @@
 		}
 	}
 
+	private static String encode(String s) {
+		final char[] a = s.toCharArray();
+		StringBuilder buf = new StringBuilder();
+		for( int i=0; i<a.length; i++ ) {
+			char c = a[i];
+			switch(c) {
+			case '&':
+				buf.append("&amp;");
+				break;
+			case '<':
+				buf.append("&lt;");
+				break;
+			case '>':
+				buf.append("&gt;");
+				break;
+			case '"':
+				buf.append("&quot;");
+				break;
+			default:
+				buf.append(c);
+			}
+		}
+		return buf.toString();
+	}
+
 }
--- a/src/goodjava/xml/XmlParser.java	Mon Apr 13 22:16:59 2020 -0600
+++ b/src/goodjava/xml/XmlParser.java	Tue Apr 14 08:44:33 2020 -0600
@@ -58,6 +58,10 @@
 			attributes.put(attribute.getKey(),attribute.getValue());
 		}
 		spaces();
+		if( parser.match("/>") ) {
+			XmlElement element = new XmlElement(name,attributes);
+			return parser.success(element);
+		}
 		required(">");
 		String s = string(name);
 		if( s != null ) {
@@ -76,6 +80,7 @@
 		int start = parser.begin();
 		while( parser.noneOf("<") );
 		String s = parser.textFrom(start);
+		s = decode(s);
 		if( !endTag(name) )
 			return parser.failure(null);
 		return parser.success(s);
@@ -125,6 +130,7 @@
 				throw exception("unclosed attribute value");
 		}
 		String value = parser.textFrom(start);
+		value = decode(value);
 		parser.match(quote);
 		Map.Entry<String,String> attribute = new AbstractMap.SimpleImmutableEntry<String,String>(name,value);
 		return parser.success(attribute);
@@ -169,4 +175,13 @@
 		return parser.anyOf(" \t\r\n");
 	}
 
+	private static String decode(String s) {
+		s = s.replace("&lt;","<");
+		s = s.replace("&gt;",">");
+		s = s.replace("&quot;","\"");
+		s = s.replace("&apos;","'");
+		s = s.replace("&amp;","&");
+		return s;
+	}
+
 }
--- a/src/luan/modules/parsers/Xml.java	Mon Apr 13 22:16:59 2020 -0600
+++ b/src/luan/modules/parsers/Xml.java	Tue Apr 14 08:44:33 2020 -0600
@@ -16,24 +16,48 @@
 public final class Xml {
 
 	public static String toString(LuanTable tbl) throws LuanException {
-		if( tbl.rawSize() != 1 )
-			throw new LuanException("XML most have 1 root element");
-		Map.Entry entry = tbl.iterator().next();
-		Object key = entry.getKey();
-		if( !(key instanceof String) )
-			throw new LuanException("XML key must be string");
-		String name = (String)key;
-		Object value = entry.getValue();
-		if( !(value instanceof LuanTable) )
-			throw new LuanException("XML root value must be table");
-		LuanTable t = (LuanTable)value;
-		Map<String,String> attributes = attributes(t);
-		XmlElement[] elements = elements(t);
-		XmlElement element = new XmlElement(name,attributes,elements);
-		return element.toString();
+		XmlElement[] elements = elements(tbl);
+		if( elements.length != 1 )
+			throw new LuanException("XML must have 1 root element");
+		return elements[0].toString();
 	}
 
-	private static final Integer ONE = new Integer(1);
+	private static final Integer ONE = Integer.valueOf(1);
+	private static final Integer TWO = Integer.valueOf(2);
+
+	private static XmlElement[] elements(LuanTable tbl) throws LuanException {
+		List<XmlElement> list = new ArrayList<XmlElement>();
+		for( Map.Entry entry : tbl.iterable() ) {
+			Object key = entry.getKey();
+			if( key.equals(ONE) )
+				continue;
+			if( !(key instanceof String) )
+				throw new LuanException("XML key must be string");
+			String name = (String)key;
+			Object value = entry.getValue();
+			XmlElement element;
+			if( value instanceof String ) {
+				String s = (String)value;
+				element = new XmlElement(name,Collections.emptyMap(),s);
+			} else {
+				LuanTable t = (LuanTable)value;
+				Map<String,String> attributes = attributes(t);
+				String s = (String)t.get(TWO);
+				if( s != null ) {
+					element = new XmlElement(name,attributes,s);
+				} else {
+					XmlElement[] elements = elements(t);
+					if( elements.length==0 ) {
+						element = new XmlElement(name,attributes);
+					} else {
+						element = new XmlElement(name,attributes,elements);
+					}
+				}
+			}
+			list.add(element);
+		}
+		return list.toArray(new XmlElement[0]);
+	}
 
 	private static Map<String,String> attributes(LuanTable tbl) throws LuanException {
 		Object obj = tbl.get(ONE);
@@ -49,43 +73,36 @@
 		return map;
 	}
 
-	private static XmlElement[] elements(LuanTable tbl) throws LuanException {
-		List<XmlElement> list = new ArrayList<XmlElement>();
-		for( Map.Entry entry : tbl.iterable() ) {
-			Object key = entry.getKey();
-			if( key.equals(ONE) )
-				continue;
-			String name = (String)key;
-			Object value = entry.getValue();
-			XmlElement element;
-			if( value instanceof String ) {
-				String s = (String)value;
-				element = new XmlElement(name,Collections.emptyMap(),s);
-			} else {
-				LuanTable t = (LuanTable)value;
-				Map<String,String> attributes = attributes(t);
-				XmlElement[] elements = elements(t);
-				element = new XmlElement(name,attributes,elements);
-			}
-			list.add(element);
-		}
-		return list.toArray(new XmlElement[0]);
-	}
-
 
 	public static LuanTable parse(Luan luan,String s) throws ParseException, LuanException {
 		XmlElement element = XmlParser.parse(s);
-		return toTable(luan,new XmlElement[]{element});
+		LuanTable tbl = new LuanTable(luan);
+		addElements(tbl,new XmlElement[]{element});
+		return tbl;
 	}
 
-	private static LuanTable toTable(Luan luan,XmlElement[] elements) throws LuanException {
-		LuanTable tbl = new LuanTable(luan);
+	private static LuanTable addElements(LuanTable tbl,XmlElement[] elements) throws LuanException {
 		for( XmlElement element : elements ) {
-			if( element.content instanceof String ) {
-				tbl.put(element.name,element.content);
+			LuanTable t = new LuanTable(tbl.luan());
+			if( !element.attributes.isEmpty() ) {
+				LuanTable attrs = new LuanTable(tbl.luan());
+				for( Map.Entry<String,String> entry : element.attributes.entrySet() ) {
+					attrs.put(entry.getKey(),entry.getValue());
+				}
+				t.put( 1, attrs );
+			}
+			if( element.content == null ) {
+				tbl.put(element.name,t);
+			} else if( element.content instanceof String ) {
+				if( t.isEmpty() ) {
+					tbl.put(element.name,element.content);
+				} else {
+					t.put( 2, element.content );
+					tbl.put(element.name,t);
+				}
 			} else {
 				XmlElement[] els = (XmlElement[])element.content;
-				LuanTable t = toTable(luan,els);
+				addElements(t,els);
 				tbl.put(element.name,t);
 			}
 		}