/* * Copyright (c) 1999-2000, Eric Moon. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions, and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Importer.cpp // e.moon 28jun99 #include "Importer.h" #include #include #include using namespace std; __USE_CORTEX_NAMESPACE // -------------------------------------------------------- // // expat hooks // -------------------------------------------------------- // void _oc_handle_start( void* pUser, const XML_Char* pName, const XML_Char** ppAtts) { ((Importer*)pUser)->xmlElementStart(pName, ppAtts); } void _oc_handle_end( void* pUser, const XML_Char* pName) { ((Importer*)pUser)->xmlElementEnd(pName); } void _oc_handle_pi( void* pUser, const XML_Char* pTarget, const XML_Char* pData) { ((Importer*)pUser)->xmlProcessingInstruction(pTarget, pData); } void _oc_handle_char( void* pUser, const XML_Char* pData, int length) { ((Importer*)pUser)->xmlCharacterData(pData, length); } void _oc_handle_default( void* pUser, const XML_Char* pData, int length) { ((Importer*)pUser)->xmlDefaultData(pData, length); } // -------------------------------------------------------- // // ctor/dtor // -------------------------------------------------------- // Importer::~Importer() { // clean up freeParser(); delete m_context; } Importer::Importer( list& errors) : m_parser(0), m_docType(0), m_identify(false), m_context(new ImportContext(errors)), m_rootObject(0) { initParser(); } Importer::Importer( ImportContext* context) : m_parser(0), m_docType(0), m_identify(false), m_context(context), m_rootObject(0) { ASSERT(m_context); initParser(); } Importer::Importer( list& errors, IPersistent* rootObject, XML::DocumentType* docType) : m_parser(0), m_docType(docType), m_identify(false), m_context(new ImportContext(errors)), m_rootObject(rootObject) { ASSERT(rootObject); ASSERT(docType); initParser(); } Importer::Importer( ImportContext* context, IPersistent* rootObject, XML::DocumentType* docType) : m_parser(0), m_docType(docType), m_identify(false), m_context(context), m_rootObject(rootObject) { ASSERT(m_context); ASSERT(rootObject); ASSERT(docType); initParser(); } // -------------------------------------------------------- // // accessors // -------------------------------------------------------- // // the import context const ImportContext& Importer::context() const { return *m_context; } // matched (or provided) document type XML::DocumentType* Importer::docType() const { return m_docType; } // completed object (available if // context().state() == ImportContext::COMPLETE, or // if a root object was provided to the ctor) IPersistent* Importer::target() const { return m_rootObject; } // -------------------------------------------------------- // // operations // -------------------------------------------------------- // // put the importer into 'identify mode' // (disengaged once the first element is encountered) void Importer::setIdentifyMode() { reset(); m_docType = 0; m_identify = true; } void Importer::reset() { // doesn't forget document type from identify cycle! m_identify = false; m_context->reset(); m_rootObject = 0; } // handle a buffer; return false if an error occurs bool Importer::parseBuffer( const char* pBuffer, uint32 length, bool last) { ASSERT(m_parser); int err = XML_Parse(m_parser, pBuffer, length, last); if(!err) { BString str = "Parse Error: "; str << XML_ErrorString(XML_GetErrorCode(m_parser)); m_context->reportError(str.String()); return false; } else return true; } // -------------------------------------------------------- // // internal operations // -------------------------------------------------------- // // create & initialize parser void Importer::initParser() { ASSERT(!m_parser); m_parser = XML_ParserCreate(0); m_context->m_pParser = m_parser; XML_SetElementHandler( m_parser, &_oc_handle_start, &_oc_handle_end); XML_SetProcessingInstructionHandler( m_parser, &_oc_handle_pi); XML_SetCharacterDataHandler( m_parser, &_oc_handle_char); XML_SetDefaultHandlerExpand( m_parser, &_oc_handle_default); XML_SetUserData( m_parser, (void*)this); } // clean up the parser void Importer::freeParser() { ASSERT(m_parser); XML_ParserFree(m_parser); m_parser = 0; } // -------------------------------------------------------- // // XML parser event hooks // -------------------------------------------------------- // void Importer::xmlElementStart( const char* pName, const char** ppAttributes) { if(m_context->m_state != ImportContext::PARSING) return; IPersistent* target = 0; if(!m_context->m_elementStack.size()) { // this is the first element; identify or verify document type if(m_rootObject) { // test against expected document type ASSERT(m_docType); if(m_docType->rootElement != pName) { BString err("Unexpected document element (should be <"); err << m_docType->rootElement << "/>"; m_context->reportError(err.String()); return; } // target the provided root object target = m_rootObject; } else { // look up doc type BAutolock _l(XML::s_docTypeLock); XML::doc_type_map::iterator it = XML::s_docTypeMap.find( BString(pName)); if(it != XML::s_docTypeMap.end()) m_docType = (*it).second; else { // whoops, don't know how to handle this element: BString err("No document type registered for element '"); err << pName << "'."; m_context->reportError(err.String()); return; } if(m_identify) { // end of identify cycle m_context->m_state = ImportContext::COMPLETE; return; } } } // at this point, there'd better be a valid document type ASSERT(m_docType); // push element name onto the stack m_context->m_elementStack.push_back(pName); // try to create an object for this element if necessary if(!target) target = m_docType->objectFor(pName); if(target) { // call 'begin import' hook m_context->m_objectStack.push_back( make_pair(m_context->element(), target)); target->xmlImportBegin(*m_context); // error? bail if(m_context->state() != ImportContext::PARSING) return; // walk attributes while(*ppAttributes) { target->xmlImportAttribute( ppAttributes[0], ppAttributes[1], *m_context); // error? bail if(m_context->state() != ImportContext::PARSING) return; ppAttributes += 2; } } else { // no object directly maps to this element; hand to // the current focus object ASSERT(m_context->m_objectStack.size()); IPersistent* curObject = m_context->m_objectStack.back().second; ASSERT(curObject); curObject->xmlImportChildBegin( pName, *m_context); // error? bail if(m_context->state() != ImportContext::PARSING) return; // walk attributes while(*ppAttributes) { curObject->xmlImportChildAttribute( ppAttributes[0], ppAttributes[1], *m_context); // error? bail if(m_context->state() != ImportContext::PARSING) return; ppAttributes += 2; } } } void Importer::xmlElementEnd( const char* pName) { if(m_context->m_state != ImportContext::PARSING) return; ASSERT(m_docType); // PRINT(("Importer::xmlElementEnd(): %s\n", pName)); // compare name to element on top of stack if(!m_context->m_elementStack.size() || m_context->m_elementStack.back() != pName) { m_context->reportError("Mismatched end tag."); return; } // see if it matches the topmost object IPersistent* pObject = 0; if(!m_context->m_objectStack.size()) { m_context->reportError("No object being constructed."); return; } if(m_context->m_objectStack.back().first == m_context->element()) { // matched; pop it pObject = m_context->m_objectStack.back().second; m_context->m_objectStack.pop_back(); } if(pObject) { // notify object that import is complete pObject->xmlImportComplete( *m_context); // error? bail if(m_context->state() != ImportContext::PARSING) return; if(m_context->m_objectStack.size()) { // hand the newly-constructed child to its parent m_context->m_objectStack.back().second->xmlImportChild( pObject, *m_context); } else { // done ASSERT(m_context->m_elementStack.size() == 1); m_context->m_state = ImportContext::COMPLETE; if(m_rootObject) { ASSERT(m_rootObject == pObject); } else m_rootObject = pObject; } } else { // notify current topmost object ASSERT(m_context->m_objectStack.size()); IPersistent* curObject = m_context->m_objectStack.back().second; ASSERT(curObject); curObject->xmlImportChildComplete( pName, *m_context); } // remove entry from element stack m_context->m_elementStack.pop_back(); ASSERT(m_context->m_objectStack.size() <= m_context->m_elementStack.size()); } void Importer::xmlProcessingInstruction( const char* pTarget, const char* pData) { if(m_context->m_state != ImportContext::PARSING) return; // PRINT(("Importer::xmlProcessingInstruction(): %s, %s\n", // pTarget, pData)); } // not 0-terminated void Importer::xmlCharacterData( const char* pData, int32 length) { if(m_context->m_state != ImportContext::PARSING) return; // see if the current element matches the topmost object IPersistent* pObject = 0; if(!m_context->m_objectStack.size()) { m_context->reportError("No object being constructed."); return; } pObject = m_context->m_objectStack.back().second; if(m_context->m_objectStack.back().first == m_context->element()) { pObject->xmlImportContent( pData, length, *m_context); } else { pObject->xmlImportChildContent( pData, length, *m_context); } } // not 0-terminated void Importer::xmlDefaultData( const char* pData, int32 length) { if(m_context->m_state != ImportContext::PARSING) return; // PRINT(("Importer::xmlDefaultData()\n")); } // END -- Importer.cpp --