Mercurial Hosting > luan
comparison src/goodjava/xml/XmlParser.java @ 1466:670b7d089699
xml support
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 13 Apr 2020 22:00:40 -0600 |
parents | |
children | 35f3bfd4f51d |
comparison
equal
deleted
inserted
replaced
1465:5e3870618377 | 1466:670b7d089699 |
---|---|
1 package goodjava.xml; | |
2 | |
3 import java.util.Map; | |
4 import java.util.AbstractMap; | |
5 import java.util.LinkedHashMap; | |
6 import java.util.List; | |
7 import java.util.ArrayList; | |
8 import goodjava.parser.Parser; | |
9 import goodjava.parser.ParseException; | |
10 | |
11 | |
12 public final class XmlParser { | |
13 | |
14 public static XmlElement parse(String text) throws ParseException { | |
15 return new XmlParser(text).parse(); | |
16 } | |
17 | |
18 private final Parser parser; | |
19 | |
20 private XmlParser(String text) { | |
21 this.parser = new Parser(text); | |
22 } | |
23 | |
24 private ParseException exception(String msg) { | |
25 return new ParseException(parser,msg); | |
26 } | |
27 | |
28 private XmlElement parse() throws ParseException { | |
29 spaces(); | |
30 prolog(); | |
31 spaces(); | |
32 XmlElement element = element(); | |
33 spaces(); | |
34 if( !parser.endOfInput() ) | |
35 throw exception("unexpected text"); | |
36 return element; | |
37 } | |
38 | |
39 private void prolog() throws ParseException { | |
40 if( !parser.match("<?xml") ) | |
41 return; | |
42 while( attribute() != null ); | |
43 spaces(); | |
44 required("?>"); | |
45 } | |
46 | |
47 private XmlElement element() throws ParseException { | |
48 parser.begin(); | |
49 if( !parser.match('<') || parser.test('/') ) | |
50 return parser.failure(null); | |
51 //spaces(); | |
52 String name = name(); | |
53 if( name==null ) | |
54 throw exception("element name not found"); | |
55 Map<String,String> attributes = new LinkedHashMap<String,String>(); | |
56 Map.Entry<String,String> attribute; | |
57 while( (attribute=attribute()) != null ) { | |
58 attributes.put(attribute.getKey(),attribute.getValue()); | |
59 } | |
60 spaces(); | |
61 required(">"); | |
62 String s = string(name); | |
63 if( s != null ) { | |
64 XmlElement element = new XmlElement(name,attributes,s); | |
65 return parser.success(element); | |
66 } | |
67 List<XmlElement> elements = elements(name); | |
68 if( elements != null ) { | |
69 XmlElement element = new XmlElement(name,attributes,elements.toArray(new XmlElement[0])); | |
70 return parser.success(element); | |
71 } | |
72 throw exception("bad element"); | |
73 } | |
74 | |
75 private String string(String name) throws ParseException { | |
76 int start = parser.begin(); | |
77 while( parser.noneOf("<") ); | |
78 String s = parser.textFrom(start); | |
79 if( !endTag(name) ) | |
80 return parser.failure(null); | |
81 return parser.success(s); | |
82 } | |
83 | |
84 private List<XmlElement> elements(String name) throws ParseException { | |
85 parser.begin(); | |
86 List<XmlElement> elements = new ArrayList<XmlElement>(); | |
87 spaces(); | |
88 XmlElement element; | |
89 while( (element=element()) != null ) { | |
90 elements.add(element); | |
91 spaces(); | |
92 } | |
93 if( !endTag(name) ) | |
94 return parser.failure(null); | |
95 return parser.success(elements); | |
96 } | |
97 | |
98 private boolean endTag(String name) throws ParseException { | |
99 parser.begin(); | |
100 if( !parser.match("</") || !parser.match(name) ) | |
101 return parser.failure(); | |
102 spaces(); | |
103 if( !parser.match('>') ) | |
104 return parser.failure(); | |
105 return parser.success(); | |
106 } | |
107 | |
108 private Map.Entry<String,String> attribute() throws ParseException { | |
109 parser.begin(); | |
110 if( !matchSpace() ) | |
111 return parser.failure(null); | |
112 spaces(); | |
113 String name = name(); | |
114 if( name==null ) | |
115 return parser.failure(null); | |
116 spaces(); | |
117 required("="); | |
118 spaces(); | |
119 if( !parser.anyOf("\"'") ) | |
120 throw exception("quote expected"); | |
121 char quote = parser.lastChar(); | |
122 int start = parser.currentIndex(); | |
123 while( !parser.test(quote) ) { | |
124 if( !parser.anyChar() ) | |
125 throw exception("unclosed attribute value"); | |
126 } | |
127 String value = parser.textFrom(start); | |
128 parser.match(quote); | |
129 Map.Entry<String,String> attribute = new AbstractMap.SimpleImmutableEntry<String,String>(name,value); | |
130 return parser.success(attribute); | |
131 } | |
132 | |
133 private String name() { | |
134 int start = parser.currentIndex(); | |
135 if( !matchNameChar() ) | |
136 return null; | |
137 while( matchNameChar() ); | |
138 return parser.textFrom(start); | |
139 } | |
140 | |
141 private boolean matchNameChar() { | |
142 return parser.inCharRange('a','z') | |
143 || parser.inCharRange('A','Z') | |
144 || parser.inCharRange('0','9') | |
145 || parser.anyOf("_.-:") | |
146 ; | |
147 } | |
148 | |
149 private void required(String s) throws ParseException { | |
150 if( !parser.match(s) ) | |
151 exception("'"+s+"' expected"); | |
152 } | |
153 | |
154 private void spaces() throws ParseException { | |
155 while( matchSpace() || matchComment() ); | |
156 } | |
157 | |
158 private boolean matchComment() throws ParseException { | |
159 if( !parser.match("<!--") ) | |
160 return false; | |
161 while( !parser.match("-->") ) { | |
162 if( !parser.anyChar() ) | |
163 throw exception("unclosed comment"); | |
164 } | |
165 return true; | |
166 } | |
167 | |
168 private boolean matchSpace() { | |
169 return parser.anyOf(" \t\r\n"); | |
170 } | |
171 | |
172 } |