1*35238bceSAndroid Build Coastguard Worker /*-------------------------------------------------------------------------
2*35238bceSAndroid Build Coastguard Worker * drawElements Quality Program Test Executor
3*35238bceSAndroid Build Coastguard Worker * ------------------------------------------
4*35238bceSAndroid Build Coastguard Worker *
5*35238bceSAndroid Build Coastguard Worker * Copyright 2014 The Android Open Source Project
6*35238bceSAndroid Build Coastguard Worker *
7*35238bceSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
8*35238bceSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
9*35238bceSAndroid Build Coastguard Worker * You may obtain a copy of the License at
10*35238bceSAndroid Build Coastguard Worker *
11*35238bceSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
12*35238bceSAndroid Build Coastguard Worker *
13*35238bceSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
14*35238bceSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
15*35238bceSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16*35238bceSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
17*35238bceSAndroid Build Coastguard Worker * limitations under the License.
18*35238bceSAndroid Build Coastguard Worker *
19*35238bceSAndroid Build Coastguard Worker *//*!
20*35238bceSAndroid Build Coastguard Worker * \file
21*35238bceSAndroid Build Coastguard Worker * \brief XML Parser.
22*35238bceSAndroid Build Coastguard Worker *//*--------------------------------------------------------------------*/
23*35238bceSAndroid Build Coastguard Worker
24*35238bceSAndroid Build Coastguard Worker #include "xeXMLParser.hpp"
25*35238bceSAndroid Build Coastguard Worker #include "deInt32.h"
26*35238bceSAndroid Build Coastguard Worker
27*35238bceSAndroid Build Coastguard Worker namespace xe
28*35238bceSAndroid Build Coastguard Worker {
29*35238bceSAndroid Build Coastguard Worker namespace xml
30*35238bceSAndroid Build Coastguard Worker {
31*35238bceSAndroid Build Coastguard Worker
32*35238bceSAndroid Build Coastguard Worker enum
33*35238bceSAndroid Build Coastguard Worker {
34*35238bceSAndroid Build Coastguard Worker TOKENIZER_INITIAL_BUFFER_SIZE = 1024
35*35238bceSAndroid Build Coastguard Worker };
36*35238bceSAndroid Build Coastguard Worker
isIdentifierStartChar(int ch)37*35238bceSAndroid Build Coastguard Worker static inline bool isIdentifierStartChar(int ch)
38*35238bceSAndroid Build Coastguard Worker {
39*35238bceSAndroid Build Coastguard Worker return de::inRange<int>(ch, 'a', 'z') || de::inRange<int>(ch, 'A', 'Z');
40*35238bceSAndroid Build Coastguard Worker }
41*35238bceSAndroid Build Coastguard Worker
isIdentifierChar(int ch)42*35238bceSAndroid Build Coastguard Worker static inline bool isIdentifierChar(int ch)
43*35238bceSAndroid Build Coastguard Worker {
44*35238bceSAndroid Build Coastguard Worker return isIdentifierStartChar(ch) || de::inRange<int>(ch, '0', '9') || (ch == '-') || (ch == '_');
45*35238bceSAndroid Build Coastguard Worker }
46*35238bceSAndroid Build Coastguard Worker
isWhitespaceChar(int ch)47*35238bceSAndroid Build Coastguard Worker static inline bool isWhitespaceChar(int ch)
48*35238bceSAndroid Build Coastguard Worker {
49*35238bceSAndroid Build Coastguard Worker return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
50*35238bceSAndroid Build Coastguard Worker }
51*35238bceSAndroid Build Coastguard Worker
getNextBufferSize(int curSize,int minNewSize)52*35238bceSAndroid Build Coastguard Worker static int getNextBufferSize(int curSize, int minNewSize)
53*35238bceSAndroid Build Coastguard Worker {
54*35238bceSAndroid Build Coastguard Worker return de::max(curSize * 2, 1 << deLog2Ceil32(minNewSize));
55*35238bceSAndroid Build Coastguard Worker }
56*35238bceSAndroid Build Coastguard Worker
Tokenizer(void)57*35238bceSAndroid Build Coastguard Worker Tokenizer::Tokenizer(void)
58*35238bceSAndroid Build Coastguard Worker : m_curToken(TOKEN_INCOMPLETE)
59*35238bceSAndroid Build Coastguard Worker , m_curTokenLen(0)
60*35238bceSAndroid Build Coastguard Worker , m_state(STATE_DATA)
61*35238bceSAndroid Build Coastguard Worker , m_buf(TOKENIZER_INITIAL_BUFFER_SIZE)
62*35238bceSAndroid Build Coastguard Worker {
63*35238bceSAndroid Build Coastguard Worker }
64*35238bceSAndroid Build Coastguard Worker
~Tokenizer(void)65*35238bceSAndroid Build Coastguard Worker Tokenizer::~Tokenizer(void)
66*35238bceSAndroid Build Coastguard Worker {
67*35238bceSAndroid Build Coastguard Worker }
68*35238bceSAndroid Build Coastguard Worker
clear(void)69*35238bceSAndroid Build Coastguard Worker void Tokenizer::clear(void)
70*35238bceSAndroid Build Coastguard Worker {
71*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_INCOMPLETE;
72*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 0;
73*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
74*35238bceSAndroid Build Coastguard Worker m_buf.clear();
75*35238bceSAndroid Build Coastguard Worker }
76*35238bceSAndroid Build Coastguard Worker
error(const std::string & what)77*35238bceSAndroid Build Coastguard Worker void Tokenizer::error(const std::string &what)
78*35238bceSAndroid Build Coastguard Worker {
79*35238bceSAndroid Build Coastguard Worker throw ParseError(what);
80*35238bceSAndroid Build Coastguard Worker }
81*35238bceSAndroid Build Coastguard Worker
feed(const uint8_t * bytes,int numBytes)82*35238bceSAndroid Build Coastguard Worker void Tokenizer::feed(const uint8_t *bytes, int numBytes)
83*35238bceSAndroid Build Coastguard Worker {
84*35238bceSAndroid Build Coastguard Worker // Grow buffer if necessary.
85*35238bceSAndroid Build Coastguard Worker if (m_buf.getNumFree() < numBytes)
86*35238bceSAndroid Build Coastguard Worker {
87*35238bceSAndroid Build Coastguard Worker m_buf.resize(getNextBufferSize(m_buf.getSize(), m_buf.getNumElements() + numBytes));
88*35238bceSAndroid Build Coastguard Worker }
89*35238bceSAndroid Build Coastguard Worker
90*35238bceSAndroid Build Coastguard Worker // Append to front.
91*35238bceSAndroid Build Coastguard Worker m_buf.pushFront(bytes, numBytes);
92*35238bceSAndroid Build Coastguard Worker
93*35238bceSAndroid Build Coastguard Worker // If we haven't parsed complete token, re-try after data feed.
94*35238bceSAndroid Build Coastguard Worker if (m_curToken == TOKEN_INCOMPLETE)
95*35238bceSAndroid Build Coastguard Worker advance();
96*35238bceSAndroid Build Coastguard Worker }
97*35238bceSAndroid Build Coastguard Worker
getChar(int offset) const98*35238bceSAndroid Build Coastguard Worker int Tokenizer::getChar(int offset) const
99*35238bceSAndroid Build Coastguard Worker {
100*35238bceSAndroid Build Coastguard Worker DE_ASSERT(de::inRange(offset, 0, m_buf.getNumElements()));
101*35238bceSAndroid Build Coastguard Worker
102*35238bceSAndroid Build Coastguard Worker if (offset < m_buf.getNumElements())
103*35238bceSAndroid Build Coastguard Worker return m_buf.peekBack(offset);
104*35238bceSAndroid Build Coastguard Worker else
105*35238bceSAndroid Build Coastguard Worker return END_OF_BUFFER;
106*35238bceSAndroid Build Coastguard Worker }
107*35238bceSAndroid Build Coastguard Worker
advance(void)108*35238bceSAndroid Build Coastguard Worker void Tokenizer::advance(void)
109*35238bceSAndroid Build Coastguard Worker {
110*35238bceSAndroid Build Coastguard Worker if (m_curToken != TOKEN_INCOMPLETE)
111*35238bceSAndroid Build Coastguard Worker {
112*35238bceSAndroid Build Coastguard Worker // Parser should not try to advance beyond end of string.
113*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_curToken != TOKEN_END_OF_STRING);
114*35238bceSAndroid Build Coastguard Worker
115*35238bceSAndroid Build Coastguard Worker // If current token is tag end, change state to data.
116*35238bceSAndroid Build Coastguard Worker if (m_curToken == TOKEN_TAG_END || m_curToken == TOKEN_EMPTY_ELEMENT_END ||
117*35238bceSAndroid Build Coastguard Worker m_curToken == TOKEN_PROCESSING_INSTRUCTION_END || m_curToken == TOKEN_COMMENT || m_curToken == TOKEN_ENTITY)
118*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
119*35238bceSAndroid Build Coastguard Worker
120*35238bceSAndroid Build Coastguard Worker // Advance buffer by length of last token.
121*35238bceSAndroid Build Coastguard Worker m_buf.popBack(m_curTokenLen);
122*35238bceSAndroid Build Coastguard Worker
123*35238bceSAndroid Build Coastguard Worker // Reset state.
124*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_INCOMPLETE;
125*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 0;
126*35238bceSAndroid Build Coastguard Worker
127*35238bceSAndroid Build Coastguard Worker // If we hit end of string here, report it as end of string.
128*35238bceSAndroid Build Coastguard Worker if (getChar(0) == END_OF_STRING)
129*35238bceSAndroid Build Coastguard Worker {
130*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_END_OF_STRING;
131*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 1;
132*35238bceSAndroid Build Coastguard Worker return;
133*35238bceSAndroid Build Coastguard Worker }
134*35238bceSAndroid Build Coastguard Worker }
135*35238bceSAndroid Build Coastguard Worker
136*35238bceSAndroid Build Coastguard Worker int curChar = getChar(m_curTokenLen);
137*35238bceSAndroid Build Coastguard Worker
138*35238bceSAndroid Build Coastguard Worker for (;;)
139*35238bceSAndroid Build Coastguard Worker {
140*35238bceSAndroid Build Coastguard Worker if (m_state == STATE_DATA)
141*35238bceSAndroid Build Coastguard Worker {
142*35238bceSAndroid Build Coastguard Worker // Advance until we hit end of buffer or tag start and treat that as data token.
143*35238bceSAndroid Build Coastguard Worker if (curChar == END_OF_STRING || curChar == (int)END_OF_BUFFER || curChar == '<' || curChar == '&')
144*35238bceSAndroid Build Coastguard Worker {
145*35238bceSAndroid Build Coastguard Worker if (curChar == '<')
146*35238bceSAndroid Build Coastguard Worker m_state = STATE_TAG;
147*35238bceSAndroid Build Coastguard Worker else if (curChar == '&')
148*35238bceSAndroid Build Coastguard Worker m_state = STATE_ENTITY;
149*35238bceSAndroid Build Coastguard Worker
150*35238bceSAndroid Build Coastguard Worker if (m_curTokenLen > 0)
151*35238bceSAndroid Build Coastguard Worker {
152*35238bceSAndroid Build Coastguard Worker // Report data token.
153*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_DATA;
154*35238bceSAndroid Build Coastguard Worker return;
155*35238bceSAndroid Build Coastguard Worker }
156*35238bceSAndroid Build Coastguard Worker else if (curChar == END_OF_STRING || curChar == (int)END_OF_BUFFER)
157*35238bceSAndroid Build Coastguard Worker {
158*35238bceSAndroid Build Coastguard Worker // Just return incomplete token, no data parsed.
159*35238bceSAndroid Build Coastguard Worker return;
160*35238bceSAndroid Build Coastguard Worker }
161*35238bceSAndroid Build Coastguard Worker else
162*35238bceSAndroid Build Coastguard Worker {
163*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_state == STATE_TAG || m_state == STATE_ENTITY);
164*35238bceSAndroid Build Coastguard Worker continue;
165*35238bceSAndroid Build Coastguard Worker }
166*35238bceSAndroid Build Coastguard Worker }
167*35238bceSAndroid Build Coastguard Worker }
168*35238bceSAndroid Build Coastguard Worker else
169*35238bceSAndroid Build Coastguard Worker {
170*35238bceSAndroid Build Coastguard Worker // Eat all whitespace if present.
171*35238bceSAndroid Build Coastguard Worker if (m_curTokenLen == 0)
172*35238bceSAndroid Build Coastguard Worker {
173*35238bceSAndroid Build Coastguard Worker while (isWhitespaceChar(curChar))
174*35238bceSAndroid Build Coastguard Worker {
175*35238bceSAndroid Build Coastguard Worker m_buf.popBack();
176*35238bceSAndroid Build Coastguard Worker curChar = getChar(0);
177*35238bceSAndroid Build Coastguard Worker }
178*35238bceSAndroid Build Coastguard Worker }
179*35238bceSAndroid Build Coastguard Worker
180*35238bceSAndroid Build Coastguard Worker // Handle end of string / buffer.
181*35238bceSAndroid Build Coastguard Worker if (curChar == END_OF_STRING)
182*35238bceSAndroid Build Coastguard Worker error("Unexpected end of string");
183*35238bceSAndroid Build Coastguard Worker else if (curChar == (int)END_OF_BUFFER)
184*35238bceSAndroid Build Coastguard Worker {
185*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_curToken == TOKEN_INCOMPLETE);
186*35238bceSAndroid Build Coastguard Worker return;
187*35238bceSAndroid Build Coastguard Worker }
188*35238bceSAndroid Build Coastguard Worker
189*35238bceSAndroid Build Coastguard Worker if (m_curTokenLen == 0)
190*35238bceSAndroid Build Coastguard Worker {
191*35238bceSAndroid Build Coastguard Worker // Expect start of identifier, value or special tag token.
192*35238bceSAndroid Build Coastguard Worker if (curChar == '\'' || curChar == '"')
193*35238bceSAndroid Build Coastguard Worker m_state = STATE_VALUE;
194*35238bceSAndroid Build Coastguard Worker else if (isIdentifierStartChar(curChar))
195*35238bceSAndroid Build Coastguard Worker m_state = STATE_IDENTIFIER;
196*35238bceSAndroid Build Coastguard Worker else if (curChar == '<' || curChar == '?' || curChar == '/')
197*35238bceSAndroid Build Coastguard Worker m_state = STATE_TAG;
198*35238bceSAndroid Build Coastguard Worker else if (curChar == '&')
199*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_state == STATE_ENTITY);
200*35238bceSAndroid Build Coastguard Worker else if (curChar == '=')
201*35238bceSAndroid Build Coastguard Worker {
202*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_EQUAL;
203*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 1;
204*35238bceSAndroid Build Coastguard Worker return;
205*35238bceSAndroid Build Coastguard Worker }
206*35238bceSAndroid Build Coastguard Worker else if (curChar == '>')
207*35238bceSAndroid Build Coastguard Worker {
208*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_TAG_END;
209*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 1;
210*35238bceSAndroid Build Coastguard Worker return;
211*35238bceSAndroid Build Coastguard Worker }
212*35238bceSAndroid Build Coastguard Worker else
213*35238bceSAndroid Build Coastguard Worker error("Unexpected character");
214*35238bceSAndroid Build Coastguard Worker }
215*35238bceSAndroid Build Coastguard Worker else if (m_state == STATE_IDENTIFIER)
216*35238bceSAndroid Build Coastguard Worker {
217*35238bceSAndroid Build Coastguard Worker if (!isIdentifierChar(curChar))
218*35238bceSAndroid Build Coastguard Worker {
219*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_IDENTIFIER;
220*35238bceSAndroid Build Coastguard Worker return;
221*35238bceSAndroid Build Coastguard Worker }
222*35238bceSAndroid Build Coastguard Worker }
223*35238bceSAndroid Build Coastguard Worker else if (m_state == STATE_VALUE)
224*35238bceSAndroid Build Coastguard Worker {
225*35238bceSAndroid Build Coastguard Worker // \todo [2012-06-07 pyry] Escapes.
226*35238bceSAndroid Build Coastguard Worker if (curChar == '\'' || curChar == '"')
227*35238bceSAndroid Build Coastguard Worker {
228*35238bceSAndroid Build Coastguard Worker // \todo [2012-10-17 pyry] Should we actually do the check against getChar(0)?
229*35238bceSAndroid Build Coastguard Worker if (curChar != getChar(0))
230*35238bceSAndroid Build Coastguard Worker error("Mismatched quote");
231*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_STRING;
232*35238bceSAndroid Build Coastguard Worker m_curTokenLen += 1;
233*35238bceSAndroid Build Coastguard Worker return;
234*35238bceSAndroid Build Coastguard Worker }
235*35238bceSAndroid Build Coastguard Worker }
236*35238bceSAndroid Build Coastguard Worker else if (m_state == STATE_COMMENT)
237*35238bceSAndroid Build Coastguard Worker {
238*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_curTokenLen >= 2); // 2 characters have been parsed if we are in comment state.
239*35238bceSAndroid Build Coastguard Worker
240*35238bceSAndroid Build Coastguard Worker if (m_curTokenLen <= 3)
241*35238bceSAndroid Build Coastguard Worker {
242*35238bceSAndroid Build Coastguard Worker if (curChar != '-')
243*35238bceSAndroid Build Coastguard Worker error("Invalid comment start");
244*35238bceSAndroid Build Coastguard Worker }
245*35238bceSAndroid Build Coastguard Worker else
246*35238bceSAndroid Build Coastguard Worker {
247*35238bceSAndroid Build Coastguard Worker int prev2 = m_curTokenLen > 5 ? getChar(m_curTokenLen - 2) : 0;
248*35238bceSAndroid Build Coastguard Worker int prev1 = m_curTokenLen > 4 ? getChar(m_curTokenLen - 1) : 0;
249*35238bceSAndroid Build Coastguard Worker
250*35238bceSAndroid Build Coastguard Worker if (prev2 == '-' && prev1 == '-')
251*35238bceSAndroid Build Coastguard Worker {
252*35238bceSAndroid Build Coastguard Worker if (curChar != '>')
253*35238bceSAndroid Build Coastguard Worker error("Invalid comment end");
254*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_COMMENT;
255*35238bceSAndroid Build Coastguard Worker m_curTokenLen += 1;
256*35238bceSAndroid Build Coastguard Worker return;
257*35238bceSAndroid Build Coastguard Worker }
258*35238bceSAndroid Build Coastguard Worker }
259*35238bceSAndroid Build Coastguard Worker }
260*35238bceSAndroid Build Coastguard Worker else if (m_state == STATE_ENTITY)
261*35238bceSAndroid Build Coastguard Worker {
262*35238bceSAndroid Build Coastguard Worker if (m_curTokenLen >= 1)
263*35238bceSAndroid Build Coastguard Worker {
264*35238bceSAndroid Build Coastguard Worker if (curChar == ';')
265*35238bceSAndroid Build Coastguard Worker {
266*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_ENTITY;
267*35238bceSAndroid Build Coastguard Worker m_curTokenLen += 1;
268*35238bceSAndroid Build Coastguard Worker return;
269*35238bceSAndroid Build Coastguard Worker }
270*35238bceSAndroid Build Coastguard Worker else if (!de::inRange<int>(curChar, '0', '9') && !de::inRange<int>(curChar, 'a', 'z') &&
271*35238bceSAndroid Build Coastguard Worker !de::inRange<int>(curChar, 'A', 'Z'))
272*35238bceSAndroid Build Coastguard Worker error("Invalid entity");
273*35238bceSAndroid Build Coastguard Worker }
274*35238bceSAndroid Build Coastguard Worker }
275*35238bceSAndroid Build Coastguard Worker else
276*35238bceSAndroid Build Coastguard Worker {
277*35238bceSAndroid Build Coastguard Worker // Special tokens are at most 2 characters.
278*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_state == STATE_TAG && m_curTokenLen == 1);
279*35238bceSAndroid Build Coastguard Worker
280*35238bceSAndroid Build Coastguard Worker int prevChar = getChar(m_curTokenLen - 1);
281*35238bceSAndroid Build Coastguard Worker
282*35238bceSAndroid Build Coastguard Worker if (prevChar == '<')
283*35238bceSAndroid Build Coastguard Worker {
284*35238bceSAndroid Build Coastguard Worker // Tag start.
285*35238bceSAndroid Build Coastguard Worker if (curChar == '/')
286*35238bceSAndroid Build Coastguard Worker {
287*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_END_TAG_START;
288*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 2;
289*35238bceSAndroid Build Coastguard Worker return;
290*35238bceSAndroid Build Coastguard Worker }
291*35238bceSAndroid Build Coastguard Worker else if (curChar == '?')
292*35238bceSAndroid Build Coastguard Worker {
293*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_PROCESSING_INSTRUCTION_START;
294*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 2;
295*35238bceSAndroid Build Coastguard Worker return;
296*35238bceSAndroid Build Coastguard Worker }
297*35238bceSAndroid Build Coastguard Worker else if (curChar == '!')
298*35238bceSAndroid Build Coastguard Worker {
299*35238bceSAndroid Build Coastguard Worker m_state = STATE_COMMENT;
300*35238bceSAndroid Build Coastguard Worker }
301*35238bceSAndroid Build Coastguard Worker else
302*35238bceSAndroid Build Coastguard Worker {
303*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_TAG_START;
304*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 1;
305*35238bceSAndroid Build Coastguard Worker return;
306*35238bceSAndroid Build Coastguard Worker }
307*35238bceSAndroid Build Coastguard Worker }
308*35238bceSAndroid Build Coastguard Worker else if (prevChar == '?')
309*35238bceSAndroid Build Coastguard Worker {
310*35238bceSAndroid Build Coastguard Worker if (curChar != '>')
311*35238bceSAndroid Build Coastguard Worker error("Invalid processing instruction end");
312*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_PROCESSING_INSTRUCTION_END;
313*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 2;
314*35238bceSAndroid Build Coastguard Worker return;
315*35238bceSAndroid Build Coastguard Worker }
316*35238bceSAndroid Build Coastguard Worker else if (prevChar == '/')
317*35238bceSAndroid Build Coastguard Worker {
318*35238bceSAndroid Build Coastguard Worker if (curChar != '>')
319*35238bceSAndroid Build Coastguard Worker error("Invalid empty element end");
320*35238bceSAndroid Build Coastguard Worker m_curToken = TOKEN_EMPTY_ELEMENT_END;
321*35238bceSAndroid Build Coastguard Worker m_curTokenLen = 2;
322*35238bceSAndroid Build Coastguard Worker return;
323*35238bceSAndroid Build Coastguard Worker }
324*35238bceSAndroid Build Coastguard Worker else
325*35238bceSAndroid Build Coastguard Worker error("Could not parse special token");
326*35238bceSAndroid Build Coastguard Worker }
327*35238bceSAndroid Build Coastguard Worker }
328*35238bceSAndroid Build Coastguard Worker
329*35238bceSAndroid Build Coastguard Worker m_curTokenLen += 1;
330*35238bceSAndroid Build Coastguard Worker curChar = getChar(m_curTokenLen);
331*35238bceSAndroid Build Coastguard Worker }
332*35238bceSAndroid Build Coastguard Worker }
333*35238bceSAndroid Build Coastguard Worker
getString(std::string & dst) const334*35238bceSAndroid Build Coastguard Worker void Tokenizer::getString(std::string &dst) const
335*35238bceSAndroid Build Coastguard Worker {
336*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_curToken == TOKEN_STRING);
337*35238bceSAndroid Build Coastguard Worker dst.resize(m_curTokenLen - 2);
338*35238bceSAndroid Build Coastguard Worker for (int ndx = 0; ndx < m_curTokenLen - 2; ndx++)
339*35238bceSAndroid Build Coastguard Worker dst[ndx] = m_buf.peekBack(ndx + 1);
340*35238bceSAndroid Build Coastguard Worker }
341*35238bceSAndroid Build Coastguard Worker
Parser(void)342*35238bceSAndroid Build Coastguard Worker Parser::Parser(void) : m_element(ELEMENT_INCOMPLETE), m_state(STATE_DATA)
343*35238bceSAndroid Build Coastguard Worker {
344*35238bceSAndroid Build Coastguard Worker }
345*35238bceSAndroid Build Coastguard Worker
~Parser(void)346*35238bceSAndroid Build Coastguard Worker Parser::~Parser(void)
347*35238bceSAndroid Build Coastguard Worker {
348*35238bceSAndroid Build Coastguard Worker }
349*35238bceSAndroid Build Coastguard Worker
clear(void)350*35238bceSAndroid Build Coastguard Worker void Parser::clear(void)
351*35238bceSAndroid Build Coastguard Worker {
352*35238bceSAndroid Build Coastguard Worker m_tokenizer.clear();
353*35238bceSAndroid Build Coastguard Worker m_elementName.clear();
354*35238bceSAndroid Build Coastguard Worker m_attributes.clear();
355*35238bceSAndroid Build Coastguard Worker m_attribName.clear();
356*35238bceSAndroid Build Coastguard Worker m_entityValue.clear();
357*35238bceSAndroid Build Coastguard Worker
358*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_INCOMPLETE;
359*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
360*35238bceSAndroid Build Coastguard Worker }
361*35238bceSAndroid Build Coastguard Worker
error(const std::string & what)362*35238bceSAndroid Build Coastguard Worker void Parser::error(const std::string &what)
363*35238bceSAndroid Build Coastguard Worker {
364*35238bceSAndroid Build Coastguard Worker throw ParseError(what);
365*35238bceSAndroid Build Coastguard Worker }
366*35238bceSAndroid Build Coastguard Worker
feed(const uint8_t * bytes,int numBytes)367*35238bceSAndroid Build Coastguard Worker void Parser::feed(const uint8_t *bytes, int numBytes)
368*35238bceSAndroid Build Coastguard Worker {
369*35238bceSAndroid Build Coastguard Worker m_tokenizer.feed(bytes, numBytes);
370*35238bceSAndroid Build Coastguard Worker
371*35238bceSAndroid Build Coastguard Worker if (m_element == ELEMENT_INCOMPLETE)
372*35238bceSAndroid Build Coastguard Worker advance();
373*35238bceSAndroid Build Coastguard Worker }
374*35238bceSAndroid Build Coastguard Worker
advance(void)375*35238bceSAndroid Build Coastguard Worker void Parser::advance(void)
376*35238bceSAndroid Build Coastguard Worker {
377*35238bceSAndroid Build Coastguard Worker if (m_element == ELEMENT_START)
378*35238bceSAndroid Build Coastguard Worker m_attributes.clear();
379*35238bceSAndroid Build Coastguard Worker
380*35238bceSAndroid Build Coastguard Worker // \note No token is advanced when element end is reported.
381*35238bceSAndroid Build Coastguard Worker if (m_state == STATE_YIELD_EMPTY_ELEMENT_END)
382*35238bceSAndroid Build Coastguard Worker {
383*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_element == ELEMENT_START);
384*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_END;
385*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
386*35238bceSAndroid Build Coastguard Worker return;
387*35238bceSAndroid Build Coastguard Worker }
388*35238bceSAndroid Build Coastguard Worker
389*35238bceSAndroid Build Coastguard Worker if (m_element != ELEMENT_INCOMPLETE)
390*35238bceSAndroid Build Coastguard Worker {
391*35238bceSAndroid Build Coastguard Worker m_tokenizer.advance();
392*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_INCOMPLETE;
393*35238bceSAndroid Build Coastguard Worker }
394*35238bceSAndroid Build Coastguard Worker
395*35238bceSAndroid Build Coastguard Worker for (;;)
396*35238bceSAndroid Build Coastguard Worker {
397*35238bceSAndroid Build Coastguard Worker Token curToken = m_tokenizer.getToken();
398*35238bceSAndroid Build Coastguard Worker
399*35238bceSAndroid Build Coastguard Worker // Skip comments.
400*35238bceSAndroid Build Coastguard Worker while (curToken == TOKEN_COMMENT)
401*35238bceSAndroid Build Coastguard Worker {
402*35238bceSAndroid Build Coastguard Worker m_tokenizer.advance();
403*35238bceSAndroid Build Coastguard Worker curToken = m_tokenizer.getToken();
404*35238bceSAndroid Build Coastguard Worker }
405*35238bceSAndroid Build Coastguard Worker
406*35238bceSAndroid Build Coastguard Worker if (curToken == TOKEN_INCOMPLETE)
407*35238bceSAndroid Build Coastguard Worker {
408*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_element == ELEMENT_INCOMPLETE);
409*35238bceSAndroid Build Coastguard Worker return;
410*35238bceSAndroid Build Coastguard Worker }
411*35238bceSAndroid Build Coastguard Worker
412*35238bceSAndroid Build Coastguard Worker switch (m_state)
413*35238bceSAndroid Build Coastguard Worker {
414*35238bceSAndroid Build Coastguard Worker case STATE_ENTITY:
415*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
416*35238bceSAndroid Build Coastguard Worker // Fall-through
417*35238bceSAndroid Build Coastguard Worker
418*35238bceSAndroid Build Coastguard Worker case STATE_DATA:
419*35238bceSAndroid Build Coastguard Worker switch (curToken)
420*35238bceSAndroid Build Coastguard Worker {
421*35238bceSAndroid Build Coastguard Worker case TOKEN_DATA:
422*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_DATA;
423*35238bceSAndroid Build Coastguard Worker return;
424*35238bceSAndroid Build Coastguard Worker
425*35238bceSAndroid Build Coastguard Worker case TOKEN_END_OF_STRING:
426*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_END_OF_STRING;
427*35238bceSAndroid Build Coastguard Worker return;
428*35238bceSAndroid Build Coastguard Worker
429*35238bceSAndroid Build Coastguard Worker case TOKEN_TAG_START:
430*35238bceSAndroid Build Coastguard Worker m_state = STATE_START_TAG_OPEN;
431*35238bceSAndroid Build Coastguard Worker break;
432*35238bceSAndroid Build Coastguard Worker
433*35238bceSAndroid Build Coastguard Worker case TOKEN_END_TAG_START:
434*35238bceSAndroid Build Coastguard Worker m_state = STATE_END_TAG_OPEN;
435*35238bceSAndroid Build Coastguard Worker break;
436*35238bceSAndroid Build Coastguard Worker
437*35238bceSAndroid Build Coastguard Worker case TOKEN_PROCESSING_INSTRUCTION_START:
438*35238bceSAndroid Build Coastguard Worker m_state = STATE_IN_PROCESSING_INSTRUCTION;
439*35238bceSAndroid Build Coastguard Worker break;
440*35238bceSAndroid Build Coastguard Worker
441*35238bceSAndroid Build Coastguard Worker case TOKEN_ENTITY:
442*35238bceSAndroid Build Coastguard Worker m_state = STATE_ENTITY;
443*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_DATA;
444*35238bceSAndroid Build Coastguard Worker parseEntityValue();
445*35238bceSAndroid Build Coastguard Worker return;
446*35238bceSAndroid Build Coastguard Worker
447*35238bceSAndroid Build Coastguard Worker default:
448*35238bceSAndroid Build Coastguard Worker error("Unexpected token");
449*35238bceSAndroid Build Coastguard Worker }
450*35238bceSAndroid Build Coastguard Worker break;
451*35238bceSAndroid Build Coastguard Worker
452*35238bceSAndroid Build Coastguard Worker case STATE_IN_PROCESSING_INSTRUCTION:
453*35238bceSAndroid Build Coastguard Worker if (curToken == TOKEN_PROCESSING_INSTRUCTION_END)
454*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
455*35238bceSAndroid Build Coastguard Worker else if (curToken != TOKEN_IDENTIFIER && curToken != TOKEN_EQUAL && curToken != TOKEN_STRING)
456*35238bceSAndroid Build Coastguard Worker error("Unexpected token in processing instruction");
457*35238bceSAndroid Build Coastguard Worker break;
458*35238bceSAndroid Build Coastguard Worker
459*35238bceSAndroid Build Coastguard Worker case STATE_START_TAG_OPEN:
460*35238bceSAndroid Build Coastguard Worker if (curToken != TOKEN_IDENTIFIER)
461*35238bceSAndroid Build Coastguard Worker error("Expected identifier");
462*35238bceSAndroid Build Coastguard Worker m_tokenizer.getTokenStr(m_elementName);
463*35238bceSAndroid Build Coastguard Worker m_state = STATE_ATTRIBUTE_LIST;
464*35238bceSAndroid Build Coastguard Worker break;
465*35238bceSAndroid Build Coastguard Worker
466*35238bceSAndroid Build Coastguard Worker case STATE_END_TAG_OPEN:
467*35238bceSAndroid Build Coastguard Worker if (curToken != TOKEN_IDENTIFIER)
468*35238bceSAndroid Build Coastguard Worker error("Expected identifier");
469*35238bceSAndroid Build Coastguard Worker m_tokenizer.getTokenStr(m_elementName);
470*35238bceSAndroid Build Coastguard Worker m_state = STATE_EXPECTING_END_TAG_CLOSE;
471*35238bceSAndroid Build Coastguard Worker break;
472*35238bceSAndroid Build Coastguard Worker
473*35238bceSAndroid Build Coastguard Worker case STATE_EXPECTING_END_TAG_CLOSE:
474*35238bceSAndroid Build Coastguard Worker if (curToken != TOKEN_TAG_END)
475*35238bceSAndroid Build Coastguard Worker error("Expected tag end");
476*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
477*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_END;
478*35238bceSAndroid Build Coastguard Worker return;
479*35238bceSAndroid Build Coastguard Worker
480*35238bceSAndroid Build Coastguard Worker case STATE_ATTRIBUTE_LIST:
481*35238bceSAndroid Build Coastguard Worker if (curToken == TOKEN_IDENTIFIER)
482*35238bceSAndroid Build Coastguard Worker {
483*35238bceSAndroid Build Coastguard Worker m_tokenizer.getTokenStr(m_attribName);
484*35238bceSAndroid Build Coastguard Worker m_state = STATE_EXPECTING_ATTRIBUTE_EQ;
485*35238bceSAndroid Build Coastguard Worker }
486*35238bceSAndroid Build Coastguard Worker else if (curToken == TOKEN_EMPTY_ELEMENT_END)
487*35238bceSAndroid Build Coastguard Worker {
488*35238bceSAndroid Build Coastguard Worker m_state = STATE_YIELD_EMPTY_ELEMENT_END;
489*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_START;
490*35238bceSAndroid Build Coastguard Worker return;
491*35238bceSAndroid Build Coastguard Worker }
492*35238bceSAndroid Build Coastguard Worker else if (curToken == TOKEN_TAG_END)
493*35238bceSAndroid Build Coastguard Worker {
494*35238bceSAndroid Build Coastguard Worker m_state = STATE_DATA;
495*35238bceSAndroid Build Coastguard Worker m_element = ELEMENT_START;
496*35238bceSAndroid Build Coastguard Worker return;
497*35238bceSAndroid Build Coastguard Worker }
498*35238bceSAndroid Build Coastguard Worker else
499*35238bceSAndroid Build Coastguard Worker error("Unexpected token");
500*35238bceSAndroid Build Coastguard Worker break;
501*35238bceSAndroid Build Coastguard Worker
502*35238bceSAndroid Build Coastguard Worker case STATE_EXPECTING_ATTRIBUTE_EQ:
503*35238bceSAndroid Build Coastguard Worker if (curToken != TOKEN_EQUAL)
504*35238bceSAndroid Build Coastguard Worker error("Expected '='");
505*35238bceSAndroid Build Coastguard Worker m_state = STATE_EXPECTING_ATTRIBUTE_VALUE;
506*35238bceSAndroid Build Coastguard Worker break;
507*35238bceSAndroid Build Coastguard Worker
508*35238bceSAndroid Build Coastguard Worker case STATE_EXPECTING_ATTRIBUTE_VALUE:
509*35238bceSAndroid Build Coastguard Worker if (curToken != TOKEN_STRING)
510*35238bceSAndroid Build Coastguard Worker error("Expected value");
511*35238bceSAndroid Build Coastguard Worker if (hasAttribute(m_attribName.c_str()))
512*35238bceSAndroid Build Coastguard Worker error("Duplicate attribute");
513*35238bceSAndroid Build Coastguard Worker
514*35238bceSAndroid Build Coastguard Worker m_tokenizer.getString(m_attributes[m_attribName]);
515*35238bceSAndroid Build Coastguard Worker m_state = STATE_ATTRIBUTE_LIST;
516*35238bceSAndroid Build Coastguard Worker break;
517*35238bceSAndroid Build Coastguard Worker
518*35238bceSAndroid Build Coastguard Worker default:
519*35238bceSAndroid Build Coastguard Worker DE_ASSERT(false);
520*35238bceSAndroid Build Coastguard Worker }
521*35238bceSAndroid Build Coastguard Worker
522*35238bceSAndroid Build Coastguard Worker m_tokenizer.advance();
523*35238bceSAndroid Build Coastguard Worker }
524*35238bceSAndroid Build Coastguard Worker }
525*35238bceSAndroid Build Coastguard Worker
getEntityValue(const std::string & entity)526*35238bceSAndroid Build Coastguard Worker static char getEntityValue(const std::string &entity)
527*35238bceSAndroid Build Coastguard Worker {
528*35238bceSAndroid Build Coastguard Worker static const struct
529*35238bceSAndroid Build Coastguard Worker {
530*35238bceSAndroid Build Coastguard Worker const char *name;
531*35238bceSAndroid Build Coastguard Worker char value;
532*35238bceSAndroid Build Coastguard Worker } s_entities[] = {
533*35238bceSAndroid Build Coastguard Worker {"<", '<'}, {">", '>'}, {"&", '&'}, {"'", '\''}, {""", '"'},
534*35238bceSAndroid Build Coastguard Worker };
535*35238bceSAndroid Build Coastguard Worker
536*35238bceSAndroid Build Coastguard Worker for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_entities); ndx++)
537*35238bceSAndroid Build Coastguard Worker {
538*35238bceSAndroid Build Coastguard Worker if (entity == s_entities[ndx].name)
539*35238bceSAndroid Build Coastguard Worker return s_entities[ndx].value;
540*35238bceSAndroid Build Coastguard Worker }
541*35238bceSAndroid Build Coastguard Worker
542*35238bceSAndroid Build Coastguard Worker return 0;
543*35238bceSAndroid Build Coastguard Worker }
544*35238bceSAndroid Build Coastguard Worker
parseEntityValue(void)545*35238bceSAndroid Build Coastguard Worker void Parser::parseEntityValue(void)
546*35238bceSAndroid Build Coastguard Worker {
547*35238bceSAndroid Build Coastguard Worker DE_ASSERT(m_state == STATE_ENTITY && m_tokenizer.getToken() == TOKEN_ENTITY);
548*35238bceSAndroid Build Coastguard Worker
549*35238bceSAndroid Build Coastguard Worker std::string entity;
550*35238bceSAndroid Build Coastguard Worker m_tokenizer.getTokenStr(entity);
551*35238bceSAndroid Build Coastguard Worker
552*35238bceSAndroid Build Coastguard Worker const char value = getEntityValue(entity);
553*35238bceSAndroid Build Coastguard Worker if (value == 0)
554*35238bceSAndroid Build Coastguard Worker error("Invalid entity '" + entity + "'");
555*35238bceSAndroid Build Coastguard Worker
556*35238bceSAndroid Build Coastguard Worker m_entityValue.resize(1);
557*35238bceSAndroid Build Coastguard Worker m_entityValue[0] = value;
558*35238bceSAndroid Build Coastguard Worker }
559*35238bceSAndroid Build Coastguard Worker
560*35238bceSAndroid Build Coastguard Worker } // namespace xml
561*35238bceSAndroid Build Coastguard Worker } // namespace xe
562