source: icXML/icXML-devel/src/icxmlc/XMLReferenceTable.cpp @ 3107

Last change on this file since 3107 was 3107, checked in by cameron, 6 years ago

MemoryManager?

File size: 21.7 KB
Line 
1#include <icxmlc/XMLReferenceTable.hpp>
2#include <icxmlc/XMLSymbolTable.hpp>
3#include <icxmlc/XMLParserDefs.hpp>
4#include <icxmlc/XMLScanIterator.hpp>
5#include <icxmlc/XMLStreamIterator.hpp>
6#include <icxmlc/XMLStringU.hpp>
7#include <xercesc/framework/XMLErrorCodes.hpp>
8#include <icxercesc/util/XMLUTF16Transcoder.hpp>
9#include <icxercesc/internal/XMLScanner.hpp>
10#include <icxercesc/internal/XMLReader.hpp>
11#include <icxmlc/XMLParser.hpp>
12#include <icxmlc/XMLWhitespaceNormalizer.hpp>
13#include <icxmlc/XMLConfig.hpp>
14// #include <cstddef>
15
16XERCES_CPP_NAMESPACE_BEGIN
17
18/// ----------------------------------------------------------------------------------------------------
19
20gid_t XMLReferenceTable::expandReference
21(
22    const XMLByte *                         entityName
23    , const size_t                          length
24    , const bool                            inAttVal
25)
26{
27    // add the ref to the appropriate table
28    XMLReference reference;
29    reference.fLength = length;
30    reference.fKey = fEntityPool.insert(entityName, length);
31    reference.fId = fReferenceTable.count();
32
33    fReferenceTable.add(reference);
34
35    if (unlikely(reference.fId == fReplacementTextList.capacity()))
36    {
37        fReplacementTextList.expand(fReplacementTextList.capacity());
38    }
39    // set this reference to "Unparsed" initially so that the system can detect whether a recursive entity expansion exists
40    // by checking whether all of its interior entities are parsed.
41    fReplacementTextList[reference.fId].fType = XMLReplacementText::Unparsed;
42
43    XMLReplacementText replacementText;
44    // this ref hasn't been seen before; ask the scanner to expand it
45    XMLBuffer entity(1023, fMemoryManager);   
46    // transcode the entity from the source document format to xerces' internal utf16 format
47    fTranscoder->transcodeFrom(entityName, length, entity);
48
49    const size_t transcodedLength = entity.getLen() - inAttVal;
50    XMLCh * referenceName = XMLString::replicate(entity.getRawBuffer(), transcodedLength, fMemoryManager);
51    referenceName[transcodedLength] = 0;
52    #ifdef PRINT_DEBUG_MESSAGE
53    replacementText.fRefNameString = XMLString::transcode(referenceName, fMemoryManager);
54    #endif
55    entity.reset();
56
57    bool expanded = 0;
58    XMLReader::XMLVersion version = fScanner.getXMLVersion();
59    // need to set these to what the CSA says they are.
60    XMLFileLoc line = 0;
61    XMLFileLoc column = 0;
62
63    //  If the next char is a pound, then its a character reference...
64    if (*referenceName == chPound)
65    {
66        const bool isXMLV1_0 = (version == XMLReader::XMLV1_0);
67
68//      3.3.3 Attribute-Value Normalization
69//      For [each] character reference, append the referenced character to the normalized value.
70
71        expanded = expandCharacterReference(referenceName + 1, transcodedLength - 1, isXMLV1_0, entity, replacementText.fType);
72    }
73    else // it must be a predefined or general entity
74    {
75        const ReplacementType replacementTextType[2] =
76        {
77            XMLReplacementText::GeneralEntity
78            , XMLReplacementText::PredefinedEntity
79        };
80
81        bool isPredefined = 0;
82        expanded = fScanner.expandEntityReference(referenceName, length, inAttVal, entity, isPredefined, version, line, column);
83        replacementText.fType = replacementTextType[isPredefined];
84    }
85
86    // then attempt to expand the entity
87    if (likely(expanded))
88    {
89        // if it's a character reference or predefined entity, we could search to see if the replacement text object already
90        // exists for the opposite context.
91
92        const bool isXMLV1_0 = (version == XMLReader::XMLV1_0);
93        // expansion succeeded; now we have to construct a content stream (and potentially a symbol gid stream)
94        expanded = parseReference(entity.getRawBuffer(), entity.getLen(), inAttVal, isXMLV1_0, replacementText);
95    }   
96
97    if (unlikely(!expanded))
98    {
99        // if we encounter an error, construct the full ref name...
100        entity.reset();
101        entity.append(chAmpersand);
102        entity.append(referenceName, length);
103        entity.append(chSemiColon);
104
105        // and then insert it into the table
106        replacementText.fType = XMLReplacementText::Error;
107        replacementText.fContentLength = entity.getLen();
108        replacementText.fContentStream = fContentPool.insert(entity.getRawBuffer(), replacementText.fContentLength);
109    }
110
111    XMLString::release(&referenceName, fMemoryManager);
112    fReplacementTextList[reference.fId] = replacementText;
113
114    return reference.fId;
115}
116
117/// ----------------------------------------------------------------------------------------------------
118
119bool XMLReferenceTable::parseReference
120(
121    XMLCh *                 replacementText
122    , size_t                length
123    , const bool            inAttVal
124    , const bool            isXMLV1_0
125    , XMLReplacementText &      toFill
126)
127{
128    bool parsed = 1;
129
130    if (likely(toFill.fType < XMLReplacementText::GeneralEntity))
131    {
132        // Since predefined entities do not need to be normalized and character references are never normalized,
133        // simply accept the replacementText as is.
134        // NOTE: character references in attribute values may expand to an illegal character, such as '<'. Check for it.
135        toFill.fContentLength = length;
136        toFill.fContentStream = fContentPool.insert(replacementText, length);
137    }
138    else if (unlikely(length != 0))
139    {
140        const bool hasOpenAngleBracket = XMLStringU::indexOf<chOpenAngle>(replacementText, length) != -1;
141        if (inAttVal | !hasOpenAngleBracket)
142        {
143            if (unlikely(inAttVal & hasOpenAngleBracket))
144            {
145                fScanner.emitError(XMLErrs::BracketInAttrValue, replacementText);
146                parsed = 0;
147            }
148            else
149            {
150                parsed = parseReferenceWithoutMarkup(replacementText, length, inAttVal, isXMLV1_0, toFill);
151            }
152        }
153        else // at least one piece of markup must exist within the replacement text
154        {
155            parsed = parseReferenceWithMarkup(replacementText, length, isXMLV1_0, toFill);
156        }
157    }
158
159    return parsed;
160}
161
162/// ----------------------------------------------------------------------------------------------------
163
164bool XMLReferenceTable::parseReferenceWithoutMarkup
165(
166    XMLCh *                         replacementText
167    , size_t                        length
168    , const bool                    inAttVal
169    , const bool                    isXMLV1_0
170    , XMLReplacementText &          toFill
171)
172{
173    InternalGidStream entityStream;
174    InternalDelimiterStream stringEndStream;
175    XMLBuffer expandedText(1023, fMemoryManager);
176    size_t entityCount = 0;
177    bool parsed = 0;
178
179    XMLWhitespaceNormalizer::normalize(replacementText, length, inAttVal, isXMLV1_0);
180
181    if (!isXMLV1_0)
182    {
183        // scan to see if any restricted characters are present
184        XMLChRangeIterator4<0x01, 0x8, 0xB, 0xC, 0xE, 0x1F, 0x7F, 0x9F> test(replacementText, length);
185        while (test.next())
186        {
187            const XMLCh toTest = replacementText[test.pos()];
188            if (likely(toTest != 0x85))
189            {
190                XMLCh tmpBuf[9];
191                XMLString::binToText
192                (
193                    toTest
194                    , tmpBuf
195                    , 8
196                    , 16
197                    , fMemoryManager
198                );
199                fScanner.emitError(XMLErrs::InvalidCharacter, tmpBuf);
200            }
201        }
202    }
203
204    // no markup but entities are possible; scan for them
205    if (unlikely(parseEntityReferences(replacementText, length, inAttVal, expandedText, entityStream, stringEndStream, entityCount)))
206    {
207        // some entity was found in the replacement text!
208        #ifdef PRINT_DEBUG_MESSAGE
209        replacementText = expandedText.getRawBuffer();
210        #else
211        replacementText = &expandedText[0];
212        #endif
213        length = expandedText.getLen();       
214    }
215
216    if (likely(length != 0))
217    {
218        toFill.fContentLength = length;       
219        toFill.fContentStream = fContentPool.insert(replacementText, length);
220
221        if (entityCount)
222        {
223            toFill.fReferenceCount = entityCount;
224            toFill.fReferenceStream = fGidPool.insert(&entityStream[0], entityCount);
225
226            toFill.fStringEndCount = entityCount;
227            WritableDelimiterPtrType delimiterStream = fStringEndPool.allocate(toFill.fStringEndCount + 1);
228            for (size_t i = 0; i < entityCount; i++)
229            {
230                delimiterStream[i] = &toFill.fContentStream[stringEndStream[i]];
231            }
232            delimiterStream[entityCount] = &toFill.fContentStream[length];
233            toFill.fStringEndStream = delimiterStream;
234
235            fSomeGeneralEntityContainsReferences = 1;
236        }
237
238        parsed = 1;
239    }
240
241
242
243    return parsed;
244}
245
246/// ----------------------------------------------------------------------------------------------------
247
248bool XMLReferenceTable::parseEntityReferences
249(
250    const XMLCh *                   replacementText
251    , size_t                        length
252    , const bool                    inAttVal
253    , XMLBuffer &                   expandedText
254    , InternalGidStream &           entityStream
255    , InternalDelimiterStream &     stringEndStream
256    , size_t &                      entityCount
257)
258{
259    bool hasRefs = 0;
260
261    XMLChIterator2<chAmpersand, chSemiColon> referenceItr(replacementText, length);
262
263    if (unlikely(referenceItr.next()))
264    {
265        size_t startPos = 0;
266        size_t endPos = 0;
267        bool found = 0;
268
269        // scan for the opening '&'
270        do
271        {
272            if (likely(replacementText[referenceItr.pos()] == chAmpersand))
273            {
274                // copy the leading replacement text
275                expandedText.set(replacementText, referenceItr.pos());
276                // we found the start of an entity reference
277                startPos = referenceItr.pos() + 1;
278                // set that we found a ref
279                found = 1;
280                break;
281            }
282        }
283        while (referenceItr.next());
284
285        hasRefs = 1;       
286
287        while (found)
288        {
289            found = (referenceItr.next() && (replacementText[endPos = referenceItr.pos()] == chSemiColon));
290
291            // scan for the closing ';'
292            if (unlikely(!found))
293            {
294                // unterminated entity! any entity must be closed with a ';' and cannot have another '&' it
295                fScanner.emitError(XMLErrs::UnterminatedEntityRef);
296                return 0;
297            }
298
299            // get the internal reference id
300            const gid_t refId = addOrFindInternal(&replacementText[startPos], (endPos - startPos) + inAttVal, inAttVal);
301            // get the reference's replacement text and modify this entity's replacement text accordingly.
302            XMLReplacementText & rto = fReplacementTextList[refId];
303            if (rto.fType < XMLReplacementText::GeneralEntity || rto.fType == XMLReplacementText::Error)
304            {
305                // copy the replacement text in directly; either we're in an attribute value entity, in which case all general
306                // entities are expanded OR we're in a content entity and it's a character reference or predefined entity.
307                expandedText.append(rto.fContentStream, rto.fContentLength);
308            }
309            else if (rto.fType == XMLReplacementText::GeneralEntity)
310            {
311                if (unlikely(entityCount == entityStream.capacity()))
312                {
313                    entityStream.expand(entityStream.capacity());
314                    stringEndStream.expand(stringEndStream.capacity());
315                }
316                entityStream[entityCount] = refId;
317                stringEndStream[entityCount] = expandedText.getLen();
318                expandedText.append(Entity);
319                entityCount++;
320            }
321            else // at an unparsed entity; thus we must have a recursive expansion.
322            {
323                reportEntity(refId, XMLErrs::RecursiveEntity);
324            }
325
326            found = 0;
327            startPos = length;
328            // scan for another opening '&'
329            while (unlikely(referenceItr.next()))
330            {
331                if (likely(replacementText[referenceItr.pos()] == chAmpersand))
332                {
333                    // we found the start of an entity reference
334                    startPos = referenceItr.pos();
335                    found = 1;
336                    break;
337                }
338            }
339
340            const ssize_t len2 = startPos - endPos - 1;
341            assert (len2 >= 0);
342
343            // copy the content between the end of the last entity and the start of the new one
344            expandedText.append(&replacementText[endPos + 1], len2);
345
346            startPos++;
347        }
348    }
349
350    return hasRefs;
351}
352
353/// -------------------------------------------------------------------------------------------------------------------
354
355//  This method scans a character reference and returns the character that
356//  was refered to. It assumes that we've already scanned the &# characters
357//  that prefix the numeric code.
358bool XMLReferenceTable::expandCharacterReference
359(
360    const XMLCh *                   characterReference
361    , size_t                        length
362    , const bool                    isXMLV1_0
363    , XMLBuffer &                   entity
364    , ReplacementType &             type
365)
366{
367    //  Set the radix. Its supposed to be a lower case x if hex. But, in
368    //  order to recover well, we check for an upper and put out an error
369    //  for that.
370    bool hexRef = 0;
371    if (*characterReference == chLatin_x)
372    {
373        hexRef = 1;
374    }
375    else if (*characterReference == chLatin_X)
376    {
377        fScanner.emitError(XMLErrs::HexRadixMustBeLowerCase);
378        hexRef = 1;
379    }
380
381    const uint8_t CONVERSION_TABLE[112] =
382        { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
383        , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
384        , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
385        ,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1
386        , -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1
387        , -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
388        , -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1
389        };
390
391    XMLUInt64 characterValue = 0;
392
393    if (hexRef)
394    {
395        if (unlikely(length <= 1))
396        {
397            fScanner.emitError(XMLErrs::UnterminatedCharRef);
398        }
399
400        length--;
401        characterReference++;
402        while (length)
403        {           
404            if (unlikely(*characterReference >= sizeof(CONVERSION_TABLE))) break;
405            const size_t value = CONVERSION_TABLE[*characterReference];
406            if (unlikely(value >= 16)) break;
407            characterValue = (characterValue << 4) | value;
408            characterReference++;
409            length--;
410        }
411    }
412    else
413    {
414        if (unlikely(length == 0))
415        {
416            fScanner.emitError(XMLErrs::UnterminatedCharRef);
417        }
418
419        while (length)
420        {           
421            if (unlikely(*characterReference >= sizeof(CONVERSION_TABLE))) break;
422            const size_t value = CONVERSION_TABLE[*characterReference];
423            if (unlikely(value >= 10)) break;
424            characterValue = (characterValue * 10) + value;
425            characterReference++;
426            length--;
427        }               
428    }
429
430    if (unlikely(length != 0))
431    {
432        XMLCh tmpStr[2];
433        tmpStr[0] = *characterReference;
434        tmpStr[1] = chNull;
435        fScanner.emitError(XMLErrs::BadDigitForRadix, tmpStr);
436    }
437
438    bool valid = true;
439    // Return the char (or chars) and check if the character expanded is valid or not
440    if (likely(characterValue <= 0xFFFF))
441    {
442        XMLCh character = XMLCh(characterValue);
443        entity.set(&character, 1);
444
445        valid = XMLNameChar::isXMLChar(characterValue, isXMLV1_0, true);
446
447        type = XMLReplacementText::CharacterReference;
448    }
449    else
450    {
451        if (likely(characterValue >= 0x10000 && characterValue <= 0x10FFFF))
452        {
453            XMLCh character[2];
454            characterValue -= static_cast<XMLUInt64>(0x10000);
455            character[0] = XMLCh((characterValue >> 10) | static_cast<XMLUInt64>(0xD800));
456            character[1] = XMLCh((characterValue & 0x3FF) | static_cast<XMLUInt64>(0xDC00));
457
458            entity.set(character, 2);
459
460            type = XMLReplacementText::SurrogateCharacterReference;
461            fHasSurrogateCharacterReferences = true;
462        }
463        else
464        {
465            valid = false;
466        }
467    }
468
469    if (unlikely(!valid))
470    {
471        // Character reference was not in the valid range
472        fScanner.emitError(XMLErrs::InvalidCharacterRef);
473    }
474
475    return valid;
476}
477
478/// ----------------------------------------------------------------------------------------------------
479
480bool XMLReferenceTable::parseReferenceWithMarkup
481(
482    XMLCh *                 replacementText
483    , size_t                length
484    , const bool            isXMLV1_0
485    , XMLReplacementText &  toFill
486)
487{
488    // should the entire replacementText be transcoded into the input character set? could simplify some code but could
489    // reduce expansion performance. Are there any character sets when converted to UTF16 and back to the input character
490    // set result in invalid characters?
491    XMLNamespaceResolver * const resolver = fScanner.getUriResolver();
492
493    {
494        // NOTE: this would likely be more efficient to do this with a byte-at-a-time system; using the UTF16CharacterSetAdapter
495        // for now to ensure that the output format is consistent even if it's changed.
496
497        XMLTranscoder * const transcoder = fSymbolTable.getTranscoder();
498        XMLUTF16CharacterSetAdapter refAdapter(*transcoder, fMemoryManager, 0);
499
500        // swap out the symbol table transcoder with a UTF16 transcoder
501        const bool resumeSymbol = fSymbolTable.fResumeSymbol;
502        fSymbolTable.fResumeSymbol = 0;
503
504        const bool resumeRef = this->fResumeRef;
505        this->fResumeRef = 0;
506
507
508        // note: the refAdapter uses the original scanner to resolve any entities, not the empty scanner
509        // used for the parsing work. That only exists to properly template the scanner.
510        XMLParserImpl<XMLScanner> refParser(&fScanner, fMemoryManager);
511        const XMLReader::XMLVersion VERSION[2] = { XMLReader::XMLV1_1, XMLReader::XMLV1_0 };
512        refParser.init(&refAdapter, transcoder, 0, 0, VERSION[isXMLV1_0]);
513        refAdapter.init(&fScanner, fSymbolTable, *this, 0, 0);
514        // parse the entity's replacement text
515        refParser.scanInternalDocumentPage(replacementText, length, toFill);
516
517        // restore the reference and symbol table's original parameters
518        fSymbolTable.fResumeSymbol = resumeSymbol;
519        this->fResumeRef = resumeRef;
520
521        /// write the value to memory
522        const XMLCh * contentStream = &refParser.fContentStream[0];
523        toFill.fContentLength = refParser.fCursorEndPtr - contentStream;
524        toFill.fContentStream = fContentPool.insert(contentStream, toFill.fContentLength);
525
526        // and store the content, gids and string ends
527        toFill.fSymbolStream = fGidPool.insert(&refParser.fSymbolStream[0], toFill.fSymbolCount);
528
529        // convert the string end pointers to point to the appropriate offset in the stored content stream
530        WritableDelimiterPtrType delimiterStream = fStringEndPool.allocate(toFill.fStringEndCount + 1);
531        for (size_t i = 0; i < toFill.fStringEndCount; i++)
532        {
533            const size_t dist = refParser.fStringEndStream[i] - contentStream;
534            delimiterStream[i] = &toFill.fContentStream[dist];
535        }
536        delimiterStream[toFill.fStringEndCount] = &toFill.fContentStream[toFill.fContentLength];
537        toFill.fStringEndStream = delimiterStream;
538
539        // check if any entities exist within this symbol; if so make sure they are parsed.
540        if (toFill.fReferenceCount)
541        {
542            toFill.fReferenceStream = fGidPool.insert(&refParser.fReferenceStream[0], toFill.fReferenceCount);
543            for (size_t i = 0; i < toFill.fReferenceCount; i++)
544            {
545                if (fReplacementTextList[toFill.fReferenceStream[i]].fType == XMLReplacementText::Unparsed)
546                {
547                    reportEntity(toFill.fReferenceStream[i], XMLErrs::RecursiveEntity);
548                }
549            }
550            fSomeGeneralEntityContainsReferences = 1;
551        }
552    }
553    while (0);
554
555    // note: the XMLParser automatically sets the UriResolver in the scanner to 0 upon deletion; this restores it.
556    fScanner.setUriResolver(resolver);
557
558    return 1;
559}
560
561/// -------------------------------------------------------------------------------------------------------------------
562
563void XMLReferenceTable::normalizeDefaultAttributeValue
564(
565    XMLElementDefaultAttribute &                 defaultAttribute
566    , const bool                                 isXMLV1_0
567)
568{
569    XMLReplacementText temp;
570
571    parseReferenceWithoutMarkup(const_cast<WritableContentPtrType>(defaultAttribute.getValue()), defaultAttribute.getValueLen(), true, isXMLV1_0, temp);
572
573    defaultAttribute.fValue = temp.fContentStream;
574    defaultAttribute.fValueLen = temp.fContentLength;
575}
576
577/// -------------------------------------------------------------------------------------------------------------------
578
579void XMLReferenceTable::reportEntity
580(
581    const gid_t                 entityId
582    , const XMLErrs::Codes      errorCode
583) const
584{
585
586    XMLBuffer entityName;
587    const XMLReference & ref = fReferenceTable[entityId];
588    fTranscoder->transcodeFrom(ref.getKey(), ref.getLength(), entityName);
589    fScanner.emitError(errorCode, entityName.getRawBuffer());
590}
591
592/// -------------------------------------------------------------------------------------------------------------------
593
594XERCES_CPP_NAMESPACE_END
Note: See TracBrowser for help on using the repository browser.