Home | History | Annotate | Download | only in qphelper
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Helper Library
      3  * -------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief XML Writer.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "qpXmlWriter.h"
     25 
     26 #include "deMemory.h"
     27 #include "deInt32.h"
     28 
     29 /*------------------------------------------------------------------------
     30  * qpXmlWriter stand-alone implementation.
     31  *----------------------------------------------------------------------*/
     32 
     33 #include "deMemPool.h"
     34 #include "dePoolArray.h"
     35 
     36 struct qpXmlWriter_s
     37 {
     38 	FILE*				outputFile;
     39 	deBool				flushAfterWrite;
     40 
     41 	deBool				xmlPrevIsStartElement;
     42 	deBool				xmlIsWriting;
     43 	int					xmlElementDepth;
     44 };
     45 
     46 static deBool writeEscaped (qpXmlWriter* writer, const char* str)
     47 {
     48 	char		buf[256 + 10];
     49 	char*		d		= &buf[0];
     50 	const char*	s		= str;
     51 	deBool		isEOS	= DE_FALSE;
     52 
     53 	do
     54 	{
     55 		/* Check for characters that need to be escaped. */
     56 		const char* repl = DE_NULL;
     57 		switch (*s)
     58 		{
     59 			case 0:		isEOS = DE_TRUE;		break;
     60 			case '<':	repl = "&lt;";			break;
     61 			case '>':	repl = "&gt;";			break;
     62 			case '&':	repl = "&amp;";			break;
     63 			case '\'':	repl = "&apos;";		break;
     64 			case '"':	repl = "&quot;";		break;
     65 
     66 			/* Non-printable characters. */
     67 			case 1:		repl = "&lt;SOH&gt;";	break;
     68 			case 2:		repl = "&lt;STX&gt;";	break;
     69 			case 3:		repl = "&lt;ETX&gt;";	break;
     70 			case 4:		repl = "&lt;EOT&gt;";	break;
     71 			case 5:		repl = "&lt;ENQ&gt;";	break;
     72 			case 6:		repl = "&lt;ACK&gt;";	break;
     73 			case 7:		repl = "&lt;BEL&gt;";	break;
     74 			case 8:		repl = "&lt;BS&gt;";	break;
     75 			case 11:	repl = "&lt;VT&gt;";	break;
     76 			case 12:	repl = "&lt;FF&gt;";	break;
     77 			case 14:	repl = "&lt;SO&gt;";	break;
     78 			case 15:	repl = "&lt;SI&gt;";	break;
     79 			case 16:	repl = "&lt;DLE&gt;";	break;
     80 			case 17:	repl = "&lt;DC1&gt;";	break;
     81 			case 18:	repl = "&lt;DC2&gt;";	break;
     82 			case 19:	repl = "&lt;DC3&gt;";	break;
     83 			case 20:	repl = "&lt;DC4&gt;";	break;
     84 			case 21:	repl = "&lt;NAK&gt;";	break;
     85 			case 22:	repl = "&lt;SYN&gt;";	break;
     86 			case 23:	repl = "&lt;ETB&gt;";	break;
     87 			case 24:	repl = "&lt;CAN&gt;";	break;
     88 			case 25:	repl = "&lt;EM&gt;";	break;
     89 			case 26:	repl = "&lt;SUB&gt;";	break;
     90 			case 27:	repl = "&lt;ESC&gt;";	break;
     91 			case 28:	repl = "&lt;FS&gt;";	break;
     92 			case 29:	repl = "&lt;GS&gt;";	break;
     93 			case 30:	repl = "&lt;RS&gt;";	break;
     94 			case 31:	repl = "&lt;US&gt;";	break;
     95 
     96 			default:	/* nada */				break;
     97 		}
     98 
     99 		/* Write out char or escape sequence. */
    100 		if (repl)
    101 		{
    102 			s++;
    103 			strcpy(d, repl);
    104 			d += strlen(repl);
    105 		}
    106 		else
    107 			*d++ = *s++;
    108 
    109 		/* Write buffer if EOS or buffer full. */
    110 		if (isEOS || ((d - &buf[0]) >= 4))
    111 		{
    112 			*d = 0;
    113 			fprintf(writer->outputFile, "%s", buf);
    114 			d = &buf[0];
    115 		}
    116 	} while (!isEOS);
    117 
    118 	if (writer->flushAfterWrite)
    119 		fflush(writer->outputFile);
    120 	DE_ASSERT(d == &buf[0]); /* buffer must be empty */
    121 	return DE_TRUE;
    122 }
    123 
    124 qpXmlWriter* qpXmlWriter_createFileWriter (FILE* outputFile, deBool useCompression, deBool flushAfterWrite)
    125 {
    126 	qpXmlWriter* writer = (qpXmlWriter*)deCalloc(sizeof(qpXmlWriter));
    127 	if (!writer)
    128 		return DE_NULL;
    129 
    130 	DE_UNREF(useCompression); /* no compression supported. */
    131 
    132 	writer->outputFile = outputFile;
    133 	writer->flushAfterWrite = flushAfterWrite;
    134 
    135 	return writer;
    136 }
    137 
    138 void qpXmlWriter_destroy (qpXmlWriter* writer)
    139 {
    140 	DE_ASSERT(writer);
    141 
    142 	deFree(writer);
    143 }
    144 
    145 static deBool closePending (qpXmlWriter* writer)
    146 {
    147 	if (writer->xmlPrevIsStartElement)
    148 	{
    149 		fprintf(writer->outputFile, ">\n");
    150 		writer->xmlPrevIsStartElement = DE_FALSE;
    151 	}
    152 
    153 	return DE_TRUE;
    154 }
    155 
    156 void qpXmlWriter_flush (qpXmlWriter* writer)
    157 {
    158 	closePending(writer);
    159 }
    160 
    161 deBool qpXmlWriter_startDocument (qpXmlWriter* writer)
    162 {
    163 	DE_ASSERT(writer && !writer->xmlIsWriting);
    164 	writer->xmlIsWriting			= DE_TRUE;
    165 	writer->xmlElementDepth			= 0;
    166 	writer->xmlPrevIsStartElement	= DE_FALSE;
    167 	fprintf(writer->outputFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    168 	return DE_TRUE;
    169 }
    170 
    171 static const char* getIndentStr (int indentLevel)
    172 {
    173 	static const char	s_indentStr[33]	= "                                ";
    174 	static const int	s_indentStrLen	= 32;
    175 	return &s_indentStr[s_indentStrLen - deMin32(s_indentStrLen, indentLevel)];
    176 }
    177 
    178 deBool qpXmlWriter_endDocument (qpXmlWriter* writer)
    179 {
    180 	DE_ASSERT(writer);
    181 	DE_ASSERT(writer->xmlIsWriting);
    182 	DE_ASSERT(writer->xmlElementDepth == 0);
    183 	closePending(writer);
    184 	writer->xmlIsWriting = DE_FALSE;
    185 	return DE_TRUE;
    186 }
    187 
    188 deBool qpXmlWriter_writeString (qpXmlWriter* writer, const char* str)
    189 {
    190 	if (writer->xmlPrevIsStartElement)
    191 	{
    192 		fprintf(writer->outputFile, ">");
    193 		writer->xmlPrevIsStartElement = DE_FALSE;
    194 	}
    195 
    196 	return writeEscaped(writer, str);
    197 }
    198 
    199 deBool qpXmlWriter_startElement(qpXmlWriter* writer, const char* elementName, int numAttribs, const qpXmlAttribute* attribs)
    200 {
    201 	int ndx;
    202 
    203 	closePending(writer);
    204 
    205 	fprintf(writer->outputFile, "%s<%s", getIndentStr(writer->xmlElementDepth), elementName);
    206 
    207 	for (ndx = 0; ndx < numAttribs; ndx++)
    208 	{
    209 		const qpXmlAttribute* attrib = &attribs[ndx];
    210 		fprintf(writer->outputFile, " %s=\"", attrib->name);
    211 		switch (attrib->type)
    212 		{
    213 			case QP_XML_ATTRIBUTE_STRING:
    214 				writeEscaped(writer, attrib->stringValue);
    215 				break;
    216 
    217 			case QP_XML_ATTRIBUTE_INT:
    218 			{
    219 				char buf[64];
    220 				sprintf(buf, "%d", attrib->intValue);
    221 				writeEscaped(writer, buf);
    222 				break;
    223 			}
    224 
    225 			case QP_XML_ATTRIBUTE_BOOL:
    226 				writeEscaped(writer, attrib->boolValue ? "True" : "False");
    227 				break;
    228 
    229 			default:
    230 				DE_ASSERT(DE_FALSE);
    231 		}
    232 		fprintf(writer->outputFile, "\"");
    233 	}
    234 
    235 	writer->xmlElementDepth++;
    236 	writer->xmlPrevIsStartElement = DE_TRUE;
    237 	return DE_TRUE;
    238 }
    239 
    240 deBool qpXmlWriter_endElement (qpXmlWriter* writer, const char* elementName)
    241 {
    242 	DE_ASSERT(writer && writer->xmlElementDepth > 0);
    243 	writer->xmlElementDepth--;
    244 
    245 	if (writer->xmlPrevIsStartElement) /* leave flag as-is */
    246 	{
    247 		fprintf(writer->outputFile, " />\n");
    248 		writer->xmlPrevIsStartElement = DE_FALSE;
    249 	}
    250 	else
    251 		fprintf(writer->outputFile, "</%s>\n", /*getIndentStr(writer->xmlElementDepth),*/ elementName);
    252 
    253 	return DE_TRUE;
    254 }
    255 
    256 deBool qpXmlWriter_writeBase64 (qpXmlWriter* writer, const deUint8* data, size_t numBytes)
    257 {
    258 	static const char s_base64Table[64] =
    259 	{
    260 		'A','B','C','D','E','F','G','H','I','J','K','L','M',
    261 		'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    262 		'a','b','c','d','e','f','g','h','i','j','k','l','m',
    263 		'n','o','p','q','r','s','t','u','v','w','x','y','z',
    264 		'0','1','2','3','4','5','6','7','8','9','+','/'
    265 	};
    266 
    267 	int			numWritten	= 0;
    268 	size_t		srcNdx		= 0;
    269 	deBool		writeIndent	= DE_TRUE;
    270 	const char*	indentStr	= getIndentStr(writer->xmlElementDepth);
    271 
    272 	DE_ASSERT(writer && data && (numBytes > 0));
    273 
    274 	/* Close and pending writes. */
    275 	closePending(writer);
    276 
    277 	/* Loop all input chars. */
    278 	while (srcNdx < numBytes)
    279 	{
    280 		size_t	numRead = (size_t)deMin32(3, (int)(numBytes - srcNdx));
    281 		deUint8	s0 = data[srcNdx];
    282 		deUint8	s1 = (numRead >= 2) ? data[srcNdx+1] : 0;
    283 		deUint8	s2 = (numRead >= 3) ? data[srcNdx+2] : 0;
    284 		char	d[5];
    285 
    286 		srcNdx += numRead;
    287 
    288 		d[0] = s_base64Table[s0 >> 2];
    289 		d[1] = s_base64Table[((s0&0x3)<<4) | (s1>>4)];
    290 		d[2] = s_base64Table[((s1&0xF)<<2) | (s2>>6)];
    291 		d[3] = s_base64Table[s2&0x3F];
    292 		d[4] = 0;
    293 
    294 		if (numRead < 3) d[3] = '=';
    295 		if (numRead < 2) d[2] = '=';
    296 
    297 		/* Write indent (if needed). */
    298 		if (writeIndent)
    299 		{
    300 			fprintf(writer->outputFile, "%s", indentStr);
    301 			writeIndent = DE_FALSE;
    302 		}
    303 
    304 		/* Write data. */
    305 		fprintf(writer->outputFile, "%s", &d[0]);
    306 
    307 		/* EOL every now and then. */
    308 		numWritten += 4;
    309 		if (numWritten >= 64)
    310 		{
    311 			fprintf(writer->outputFile, "\n");
    312 			numWritten = 0;
    313 			writeIndent = DE_TRUE;
    314 		}
    315 	}
    316 
    317 	/* Last EOL. */
    318 	if (numWritten > 0)
    319 		fprintf(writer->outputFile, "\n");
    320 
    321 	DE_ASSERT(srcNdx == numBytes);
    322 	return DE_TRUE;
    323 }
    324 
    325 /* Common helper functions. */
    326 
    327 deBool qpXmlWriter_writeStringElement (qpXmlWriter* writer, const char* elementName, const char* elementContent)
    328 {
    329 	if (!qpXmlWriter_startElement(writer, elementName, 0, DE_NULL) ||
    330 		(elementContent && !qpXmlWriter_writeString(writer, elementContent)) ||
    331 		!qpXmlWriter_endElement(writer, elementName))
    332 		return DE_FALSE;
    333 
    334 	return DE_TRUE;
    335 }
    336