Mercurial Hosting > nabble
comparison src/nabble/naml/dom/ParserImpl.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7ecd1a4ef557 |
---|---|
1 package nabble.naml.dom; | |
2 | |
3 import org.slf4j.Logger; | |
4 import org.slf4j.LoggerFactory; | |
5 import java.util.List; | |
6 import java.util.ArrayList; | |
7 | |
8 | |
9 final class ParserImpl implements Parser { | |
10 private static final Logger logger = LoggerFactory.getLogger(ParserImpl.class); | |
11 | |
12 private int startingLine = 0; | |
13 private int i; | |
14 private int iLine; | |
15 private int line; | |
16 private int len; | |
17 private String text; | |
18 private ParserImpl subParser = null; | |
19 | |
20 public Naml parse(String source) throws ParseException { | |
21 i = 0; | |
22 iLine = 0; | |
23 line = startingLine; | |
24 len = source.length(); | |
25 text = source; | |
26 return doParse(null).naml; | |
27 } | |
28 | |
29 private static class Return { | |
30 final Naml naml; | |
31 final String spaceAtEndOfClosingTag; | |
32 | |
33 Return(Naml naml,String spaceAtEndOfClosingTag) { | |
34 this.naml = naml; | |
35 this.spaceAtEndOfClosingTag = spaceAtEndOfClosingTag; | |
36 } | |
37 } | |
38 | |
39 private Return doParse(String tagName) throws ParseException { | |
40 int start = i; | |
41 Naml naml = new Naml(); | |
42 int i2; | |
43 while( i < len ) { | |
44 i2 = text.indexOf('<',i); | |
45 if( i2 == -1 ) { | |
46 if( tagName != null ) | |
47 throw parseException("unclosed tag: "+tagName,start); | |
48 naml.add( text.substring(i).intern() ); | |
49 return new Return(naml,null); | |
50 } | |
51 if( i < i2 ) | |
52 naml.add( text.substring(i,i2).intern() ); | |
53 if( text.startsWith("<!--",i2) ) { | |
54 i = text.indexOf("-->",i2); | |
55 if( i < i2+4 ) | |
56 throw parseException("unclosed comment",i2); | |
57 naml.add( new Comment( text.substring(i2+4,i) ) ); | |
58 i += 3; | |
59 } else if( text.startsWith("<![CDATA[",i2) ) { | |
60 i = text.indexOf("]]>",i2); | |
61 if( i == -1 ) | |
62 throw parseException("unclosed CDATA",i2); | |
63 naml.add( new Cdata( text.substring(i2+9,i) ) ); | |
64 i += 3; | |
65 } else { | |
66 i = text.indexOf('>',i2); | |
67 if( i == -1 ) | |
68 throw parseException("unclosed tag",i2); | |
69 setLine(i2); | |
70 if( text.charAt(i2+1) == '/' ) { | |
71 int nameStart = i2 + 2; | |
72 int nameEnd = nameEnd(nameStart); | |
73 int spaceEnd = skipSpace(nameEnd); | |
74 if( spaceEnd != i ) | |
75 throw parseException("illegal char",spaceEnd); | |
76 if( !text.substring(nameStart,nameEnd).equals(tagName) ) { | |
77 if( tagName == null ) | |
78 throw parseException("unmatched closing tag: "+text.substring(i2,i+1),i2); | |
79 else | |
80 throw parseException("closing tag "+text.substring(i2,i+1)+" doesn't match expected </"+tagName+">",i2); | |
81 } | |
82 i++; | |
83 return new Return(naml,text.substring(nameEnd,spaceEnd)); | |
84 } | |
85 int startLine = line; | |
86 if( text.charAt(i-1) == '/' ) { | |
87 TagInfo tagInfo = new TagInfo(i2+1,i-1); | |
88 if( tagInfo.name.endsWithDot() ) | |
89 throw parseException("empty tag can't end with dot: "+tagInfo.tagName,i); | |
90 naml.add( new EmptyElement(tagInfo.name,tagInfo.attributes,tagInfo.spaceAtEnd,startLine) ); | |
91 i++; | |
92 } else { | |
93 TagInfo tagInfo = new TagInfo(i2+1,i); | |
94 i++; | |
95 String s = tagInfo.tagName.toLowerCase(); | |
96 Return r = doParse(tagInfo.tagName); | |
97 naml.add( new Container(tagInfo.name,tagInfo.attributes,tagInfo.spaceAtEnd,startLine,r.naml,r.spaceAtEndOfClosingTag) ); | |
98 } | |
99 } | |
100 } | |
101 if( tagName != null ) | |
102 throw parseException("unclosed tag: "+tagName,start); | |
103 return new Return(naml,null); | |
104 } | |
105 | |
106 private class TagInfo { | |
107 final String tagName; | |
108 final ElementName name; | |
109 final List<Attribute> attributes = new ArrayList<Attribute>(); | |
110 final String spaceAtEnd; | |
111 | |
112 TagInfo(int start,int end) throws ParseException { | |
113 int i = nameEnd(start); | |
114 tagName = text.substring(start,i); | |
115 name = new ElementName(tagName); | |
116 int afterSpace; | |
117 while( (afterSpace = skipSpace(i)) < end ) { | |
118 int afterName = nameEnd(afterSpace); | |
119 int afterSpaceAfterName = skipSpace(afterName); | |
120 if( text.charAt(afterSpaceAfterName) != '=' ) | |
121 throw parseException("expected '=' not found for attribute '"+text.substring(afterSpace,afterName)+"'",afterSpaceAfterName); | |
122 int beforeSpaceBeforeValue = afterSpaceAfterName + 1; | |
123 int afterSpaceBeforeValue = skipSpace(beforeSpaceBeforeValue); | |
124 char quote = text.charAt(afterSpaceBeforeValue); | |
125 if( quote != '"' && quote != '\'' ) | |
126 throw parseException("missing quote",afterSpaceBeforeValue); | |
127 int startValue = afterSpaceBeforeValue + 1; | |
128 int endValue = startValue; | |
129 while(true) { | |
130 if( endValue == end ) | |
131 throw parseException("missing closing quote",endValue); | |
132 char c = text.charAt(endValue); | |
133 if( c == quote ) | |
134 break; | |
135 if( c == '<' || c == '>' ) | |
136 throw parseException("invalid quoted char '"+c+"'",endValue); | |
137 endValue++; | |
138 } | |
139 String spaceBeforeName = text.substring(i,afterSpace); | |
140 String name = text.substring(afterSpace,afterName); | |
141 String spaceAfterName = text.substring(afterName,afterSpaceAfterName); | |
142 String spaceBeforeValue = text.substring(beforeSpaceBeforeValue,afterSpaceBeforeValue); | |
143 String valueStr = text.substring(startValue,endValue); | |
144 Naml value; | |
145 if( quote == '"' ) { | |
146 setLine(startValue); | |
147 ParserImpl parser = parser(); | |
148 String source = valueStr.replace('[','<').replace(']','>'); | |
149 value = parser.parse(source); | |
150 } else { | |
151 value = new Naml(); | |
152 value.add(valueStr); | |
153 } | |
154 attributes.add( new Attribute(spaceBeforeName,name,spaceAfterName,spaceBeforeValue,value,quote) ); | |
155 i = endValue + 1; | |
156 } | |
157 spaceAtEnd = text.substring(i,end); | |
158 } | |
159 } | |
160 | |
161 private ParserImpl parser() { | |
162 if( subParser == null ) | |
163 subParser = new ParserImpl(); | |
164 subParser.startingLine = line; | |
165 return subParser; | |
166 } | |
167 | |
168 private int skipSpace(int i) { | |
169 while( i<len && Character.isWhitespace(text.charAt(i)) ) | |
170 i++; | |
171 return i; | |
172 } | |
173 | |
174 private int nameEnd(int i) throws ParseException { | |
175 if( i >= len ) | |
176 throw parseException("name not found",i); | |
177 char c = text.charAt(i); | |
178 if( !( Character.isLetter(c) || c=='_' ) ) | |
179 throw parseException("invalid char: '"+c+"'",i); | |
180 while( ++i < len ) { | |
181 c = text.charAt(i); | |
182 if( !( Character.isLetterOrDigit(c) || c=='_' || c=='.' || c=='-' || c==':' ) ) | |
183 break; | |
184 } | |
185 return i; | |
186 } | |
187 | |
188 private void setLine(int i) { | |
189 line += lines(iLine,i); | |
190 iLine = i; | |
191 } | |
192 | |
193 private int lines(int start,int end) { | |
194 int n = 0; | |
195 int i = start - 1; | |
196 while(true) { | |
197 i = text.indexOf('\n',i+1); | |
198 if( i == -1 || i >= end ) | |
199 return n; | |
200 n++; | |
201 } | |
202 } | |
203 | |
204 private ParseException parseException(String msg,int i) { | |
205 return new ParseException(msg,startingLine+lines(0,i)); | |
206 } | |
207 | |
208 } |