changeset 1790:a8c685a894b4

start xml work
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 25 Dec 2023 23:07:59 -0700
parents bac27b119ff2
children f8f5c51f5b36
files src/goodjava/xml/Xml.java src/goodjava/xml/XmlElement.java src/goodjava/xml/XmlParser.java src/luan/modules/Xml.luan src/luan/modules/parsers/Xml.java
diffstat 5 files changed, 139 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/goodjava/xml/Xml.java	Mon Dec 25 23:07:59 2023 -0700
@@ -0,0 +1,27 @@
+package goodjava.xml;
+
+
+public final class Xml {
+	public String declaration;
+	private XmlElement element;
+
+	public XmlElement getElement() {
+		return element;
+	}
+
+	public void setElement(XmlElement element) {
+		if( element == null )
+			throw new IllegalArgumentException("element can't be null");
+		this.element = element;
+	}
+
+	@Override public String toString() {
+		StringBuilder sb = new StringBuilder();
+		if( declaration != null ) {
+			sb.append( declaration );
+			sb.append( '\n' );
+		}
+		element.toString(sb,0);
+		return sb.toString();
+	}
+}
--- a/src/goodjava/xml/XmlElement.java	Sun Dec 24 16:52:35 2023 -0700
+++ b/src/goodjava/xml/XmlElement.java	Mon Dec 25 23:07:59 2023 -0700
@@ -6,7 +6,7 @@
 public final class XmlElement {
 	public final String name;
 	public final Map<String,String> attributes;
-	public final Object content;
+	private Object content = null;
 
 	public XmlElement(String name,Map<String,String> attributes) {
 		this.name = name;
@@ -14,31 +14,35 @@
 		this.content = null;
 	}
 
-	public XmlElement(String name,Map<String,String> attributes,String content) {
+	public Object getContent() {
+		return content;
+	}
+
+	public void removeContent() {
+		content = null;
+	}
+
+	public void setContent(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) {
+	public void setContent(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;
 	}
 
-	public String toString() {
+	@Override public String toString() {
 		StringBuilder sb = new StringBuilder();
 		toString(sb,0);
 		return sb.toString();
 	}
 
-	private void toString(StringBuilder sb,int indented) {
+	void toString(StringBuilder sb,int indented) {
 		indent(sb,indented);
 		sb.append( '<' );
 		sb.append( name );
@@ -100,6 +104,12 @@
 			case '\'':
 				buf.append("&apos;");
 				break;
+			case '\n':
+				buf.append("&#10;");
+				break;
+			case '\r':
+				buf.append("&#13;");
+				break;
 			default:
 				buf.append(c);
 			}
--- a/src/goodjava/xml/XmlParser.java	Sun Dec 24 16:52:35 2023 -0700
+++ b/src/goodjava/xml/XmlParser.java	Mon Dec 25 23:07:59 2023 -0700
@@ -5,13 +5,15 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
 import goodjava.parser.Parser;
 import goodjava.parser.ParseException;
 
 
 public final class XmlParser {
 
-	public static XmlElement parse(String text) throws ParseException {
+	public static Xml parse(String text) throws ParseException {
 		return new XmlParser(text).parse();
 	}
 
@@ -25,9 +27,10 @@
 		return new ParseException(parser,msg);
 	}
 
-	private XmlElement parse() throws ParseException {
+	private Xml parse() throws ParseException {
+		Xml xml = new Xml();
 		spaces();
-		prolog();
+		xml.declaration = declaration();
 		spaces();
 		XmlElement element = element();
 		spaces();
@@ -35,15 +38,19 @@
 			throw exception("unexpected text");
 		if( element==null )
 			throw exception("empty document not allowed");
-		return element;
+		xml.setElement(element);
+		return xml;
 	}
 
-	private void prolog() throws ParseException {
+	private String declaration() throws ParseException {
+		int start = parser.begin();
 		if( !parser.match("<?xml") )
-			return;
+			return parser.failure(null);
 		while( attribute() != null );
 		spaces();
 		required("?>");
+		String s = parser.textFrom(start);
+		return parser.success(s);
 	}
 
 	private XmlElement element() throws ParseException {
@@ -67,12 +74,14 @@
 		required(">");
 		String s = string(name);
 		if( s != null ) {
-			XmlElement element = new XmlElement(name,attributes,s);
+			XmlElement element = new XmlElement(name,attributes);
+			element.setContent(s);
 			return parser.success(element);
 		}
 		List<XmlElement> elements = elements(name);
 		if( elements != null ) {
-			XmlElement element = new XmlElement(name,attributes,elements.toArray(new XmlElement[0]));
+			XmlElement element = new XmlElement(name,attributes);
+			element.setContent( elements.toArray(new XmlElement[0]) );
 			return parser.success(element);
 		}
 		throw exception("bad element");
@@ -177,7 +186,19 @@
 		return parser.anyOf(" \t\r\n");
 	}
 
+	private static final Pattern dec = Pattern.compile("&#(\\d+);");
+
 	private static String decode(String s) {
+		Matcher m = dec.matcher(s);
+		StringBuffer sb = new StringBuffer();
+		while( m.find() ) {
+			String d = m.group(1);
+			int i = Integer.parseInt(d);
+			d = String.valueOf((char)i);
+			m.appendReplacement(sb, d);
+		}
+		m.appendTail(sb);
+		s = sb.toString();
 		s = s.replace("&lt;","<");
 		s = s.replace("&gt;",">");
 		s = s.replace("&quot;","\"");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/Xml.luan	Mon Dec 25 23:07:59 2023 -0700
@@ -0,0 +1,49 @@
+require "java"
+local XmlParser = require "java:goodjava.xml.XmlParser"
+local XmlElement = require "java:goodjava.xml.XmlElement"
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local set_metatable = Luan.set_metatable or error()
+
+
+local Xml = {}
+
+Xml.encode = XmlElement.encode
+
+local function new(java,mt)
+	local t = {}
+	t.java = java
+	set_metatable(t,mt)
+	return t
+end
+
+local element_mt = {}
+
+local xml_mt = {}
+function xml_mt.__index(xml,key)
+	if key == "element" then
+		return new( xml.java.element, element_mt )
+	end
+	if key == "declaration" then
+		return xml.java.declaration
+	end
+	return nil
+end
+function xml_mt.__new_index(xml,key,value)
+	if key == "element" then
+		xml.java.setElement( value.java )
+	elseif key == "declaration" then
+		xml.java.declaration = value
+	else
+		error("invalid key: "..key)
+	end
+end
+function xml_mt.__to_string(xml)
+	return xml.java.toString()
+end
+
+function Xml.parse(text)
+	return new( XmlParser.parse(text), xml_mt )
+end
+
+return Xml
--- a/src/luan/modules/parsers/Xml.java	Sun Dec 24 16:52:35 2023 -0700
+++ b/src/luan/modules/parsers/Xml.java	Mon Dec 25 23:07:59 2023 -0700
@@ -52,19 +52,25 @@
 
 	private static XmlElement element(String name,Object obj) throws LuanException {
 		if( obj instanceof String ) {
-			return new XmlElement( name, Collections.emptyMap(), (String)obj );
+			XmlElement element = new XmlElement( name, Collections.emptyMap() );
+			element.setContent( (String)obj );
+			return element;
 		}
 		LuanTable t = (LuanTable)obj;
 		Map<String,String> attributes = attributes(t);
 		String s = (String)t.rawGet(TEXT);
 		if( s != null ) {
-			return new XmlElement(name,attributes,s);
+			XmlElement element = new XmlElement(name,attributes);
+			element.setContent(s);
+			return element;
 		} else {
 			XmlElement[] elements = elements(t);
 			if( elements.length==0 ) {
 				return new XmlElement(name,attributes);
 			} else {
-				return new XmlElement(name,attributes,elements);
+				XmlElement element = new XmlElement(name,attributes);
+				element.setContent(elements);
+				return element;
 			}
 		}
 	}
@@ -85,7 +91,7 @@
 
 
 	public static LuanTable parse(String s) throws ParseException, LuanException {
-		XmlElement element = XmlParser.parse(s);
+		XmlElement element = XmlParser.parse(s).getElement();
 		LuanTable tbl = new LuanTable();
 		addElements(tbl,new XmlElement[]{element});
 		return tbl;
@@ -101,12 +107,13 @@
 				}
 				t.rawPut( ATTRIBUTES, attrs );
 			}
-			if( element.content == null ) {
+			Object content = element.getContent();
+			if( content == null ) {
 				// nothing
-			} else if( element.content instanceof String ) {
-				t.rawPut( TEXT, element.content );
+			} else if( content instanceof String ) {
+				t.rawPut( TEXT, content );
 			} else {
-				XmlElement[] els = (XmlElement[])element.content;
+				XmlElement[] els = (XmlElement[])content;
 				addElements(t,els);
 			}
 			LuanTable old = (LuanTable)tbl.rawGet(element.name);