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