text: // =================================================================================================
  // ADOBE SYSTEMS INCORPORATED
  // Copyright 2015 Adobe Systems Incorporated
  // All Rights Reserved
  //
  // NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
  // of the Adobe license agreement accompanying it.
  // =================================================================================================
  
  #define IMPLEMENTATION_HEADERS_CAN_BE_INCLUDED 1
  	#include "XMPCore/ImplHeaders/DOMParserImpl.h"
  	#include "XMPCore/ImplHeaders/ClientDOMParserWrapperImpl.h"
  #undef IMPLEMENTATION_HEADERS_CAN_BE_INCLUDED
  
  #include "XMPCommon/Interfaces/IError_I.h"
  #include "XMPCore/XMPCoreErrorCodes.h"
  #include "XMPCore/Interfaces/IArrayNode_I.h"
  #include "XMPCore/Interfaces/IMetadata_I.h"
  #include "XMPCore/Interfaces/INodeIterator.h"
  #include "XMPCommon/Interfaces/ISharedMutex.h"
  #include "XMPCommon/Utilities/AutoSharedLock.h"
  #include "XMPCommon/Utilities/TSmartPointers_I.h"
  
  namespace AdobeXMPCore_Int {
  
  	DOMParserImpl::DOMParserImpl()
  		: mSharedMutex( ISharedMutex::CreateSharedMutex() ) { }
  
  	spIDOMParser APICALL DOMParserImpl::Clone() const {
  		DOMParserImpl * cloned = clone();
  		if ( cloned ) {
  			AutoSharedLock lock( mSharedMutex );
  			cloned->mTreatKeyAsCaseInsensitiveCharBuffer = mTreatKeyAsCaseInsensitiveCharBuffer;
  			cloned->mAllowDifferentValueTypesForExistingEntries = mAllowDifferentValueTypesForExistingEntries;
  			if ( mKeysSet )
  				cloned->mKeysSet = new KeysSet( mKeysSet->begin(), mKeysSet->end() );
  
  			if ( mKeyValueTypeMap )
  				cloned->mKeyValueTypeMap = new keyValueTypeMap( mKeyValueTypeMap->begin(), mKeyValueTypeMap->end() );
  
  			for ( auto it = mMap.begin(), itEnd = mMap.end(); it != itEnd; ++it ) {
  				cloned->mMap[ it->first ] = it->second;
  			}
  		}
  		return MakeUncheckedSharedPointer( cloned, __FILE__, __LINE__, true );
  	}
  
  	spIMetadata APICALL DOMParserImpl::Parse( const char * buffer, sizet bufferLength ) {
  		auto node = ParseAsNode( buffer, bufferLength );
  		if ( node ) {
  			switch ( node->GetNodeType() ) {
  			case INode::kNTSimple:
  			case INode::kNTArray:
  				{
  					spIMetadata meta = IMetadata::CreateMetadata();
  					meta->AppendNode( node );
  					return meta;
  				}
  				break;
  
  			case INode::kNTStructure:
  				{
  					pIMetadata meta( NULL );
  					try {
  						meta = node->GetInterfacePointer< IMetadata >();
  					} catch ( spcIError err ) {
  						meta = NULL;
  					}
  					if ( meta ) {
  						return MakeUncheckedSharedPointer( meta, __FILE__, __LINE__ );
  					} else {
  						spIMetadata meta = IMetadata::CreateMetadata();
  						meta->AppendNode( node );
  						return meta;
  					}
  				}
  				break;
  
  			default:
  				NOTIFY_ERROR( IError::kEDGeneral, kGECInternalFailure, "Unhandled situation occured", IError::kESOperationFatal, false, false );
  			}
  		}
  		return spIMetadata();
  	}
  
  	static void AppendAsChildren( const spINode & contextNode, const spINode & parsedNode ) {
  		if ( !contextNode )
  			NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false );
  		auto contextNodeType = contextNode->GetNodeType();
  		if ( contextNodeType != INode::kNTStructure && contextNodeType != INode::kNTArray )
  			NOTIFY_ERROR( IError::kEDParser, kPECContextNodeIsNonComposite, "Context Node is non composite", IError::kESOperationFatal,
  				true, static_cast< sizet >( contextNodeType ) );
  		pICompositeNode compositeContextNode = contextNode->GetInterfacePointer< ICompositeNode >();
  		pIMetadata meta( NULL );
  		try {
  			meta = parsedNode->GetInterfacePointer< IMetadata >();
  		} catch ( spcIError err ) {
  			meta = NULL;
  		}
  		if ( meta ) {
  			auto it = meta->Iterator();
  			while ( it ) {
  				auto childNode = it->GetNode();
  				it = it->Next();
  				childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() );
  				compositeContextNode->AppendNode( childNode );
  			}
  		} else {
  			compositeContextNode->AppendNode( parsedNode );
  		}
  	}
  
  	static void ReplaceChildren( pIArrayNode contextArrayNode, const spINode & parsedNode ) {
  		contextArrayNode->Clear();
  		pIMetadata meta( NULL );
  		try {
  			meta = parsedNode->GetInterfacePointer< IMetadata >();
  		} catch ( spcIError err ) {
  			meta = NULL;
  		}
  		if ( meta ) {
  			auto it = meta->Iterator();
  			while ( it ) {
  				auto childNode = it->GetNode();
  				it = it->Next();
  				childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() );
  				contextArrayNode->AppendNode( childNode );	
  			}
  		} else {
  			contextArrayNode->AppendNode( parsedNode );
  		}
  	}
  
  	static void ReplaceChildren( pIStructureNode contextStructureNode, const spINode & parsedNode ) {
  		pIMetadata meta( NULL );
  		try {
  			meta = parsedNode->GetInterfacePointer< IMetadata >();
  		} catch ( spcIError err ) {
  			meta = NULL;
  		}
  		if ( meta ) {
  			auto it = meta->Iterator();
  			while ( it ) {
  				auto childNode = it->GetNode();
  				it = it->Next();
  				childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() );
  				contextStructureNode->ReplaceNode( childNode );		
  			}
  		} else {
  			contextStructureNode->ReplaceNode( parsedNode );
  		}
  	}
  
  	static void ReplaceChildren( const spINode & contextNode, const spINode & parsedNode ) {
  		if ( !contextNode )
  			NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false );
  		auto contextNodeType = contextNode->GetNodeType();
  		if ( contextNodeType != INode::kNTStructure && contextNodeType != INode::kNTArray )
  			NOTIFY_ERROR( IError::kEDParser, kPECContextNodeIsNonComposite, "Context Node is non composite", IError::kESOperationFatal,
  				true, static_cast< sizet >( contextNodeType ) );
  		switch ( contextNodeType ) {
  		case INode::kNTArray:
  			ReplaceChildren( contextNode->GetInterfacePointer< IArrayNode >(), parsedNode );
  			break;
  
  		case INode::kNTStructure:
  			ReplaceChildren( contextNode->GetInterfacePointer< IStructureNode >(), parsedNode );
  			break;
  		}
  	}
  
  	static void AppendOrReplaceChildren( pIStructureNode contextStructureNode, const spINode & parsedNode ) {
  		pIMetadata meta( NULL );
  		try {
  			meta = parsedNode->GetInterfacePointer< IMetadata >();
  		} catch ( spcIError err ) {
  			meta = NULL;
  		}
  		if ( meta ) {
  			auto it = meta->Iterator();
  			while ( it ) {
  				auto childNode = it->GetNode();
  				it = it->Next();
  				childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() );
  				if ( contextStructureNode->GetIStructureNode_I()->GetNode( childNode->GetNameSpace(), childNode->GetName() ) )
  					contextStructureNode->ReplaceNode( childNode );
  				else
  					contextStructureNode->AppendNode( childNode );	
  			}
  		} else {
  			if ( contextStructureNode->GetIStructureNode_I()->GetNode( parsedNode->GetNameSpace(), parsedNode->GetName() ) )
  				contextStructureNode->ReplaceNode( parsedNode );
  			else
  				contextStructureNode->AppendNode( parsedNode );
  		}
  	}
  
  	static void AppendOrReplaceChildren( const spINode & contextNode, const spINode & parsedNode ) {
  		if ( !contextNode )
  			NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false );
  		auto contextNodeType = contextNode->GetNodeType();
  		if ( contextNodeType != INode::kNTStructure && contextNodeType != INode::kNTArray )
  			NOTIFY_ERROR( IError::kEDParser, kPECContextNodeIsNonComposite, "Context Node is non composite", IError::kESOperationFatal,
  				true, static_cast< sizet >( contextNodeType ) );
  		switch ( contextNodeType ) {
  		case INode::kNTArray:
  			ReplaceChildren( contextNode->GetInterfacePointer< IArrayNode >(), parsedNode );
  			break;
  
  		case INode::kNTStructure:
  			AppendOrReplaceChildren( contextNode->GetInterfacePointer< IStructureNode >(), parsedNode );
  			break;
  		}
  	}
  
  	static void InsertBefore( const spINode & contextNode, const spINode & parsedNode ) {
  		if ( !contextNode )
  			NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false );
  		if ( !contextNode->IsArrayItem() )
  			NOTIFY_ERROR( IError::kEDParser, kPECContextNodeParentIsNonArray, "Context Node's Parent is non array node", IError::kESOperationFatal, false, false );
  		pIMetadata meta( NULL );
  		try {
  			meta = parsedNode->GetInterfacePointer< IMetadata >();
  		} catch ( spcIError err ) {
  			meta = NULL;
  		}
  		pIArrayNode parentArrayNode = contextNode->GetINode_I()->GetRawParentPointer()->GetInterfacePointer< IArrayNode >();
  		if ( meta ) {
  			auto it = meta->Iterator();
  			while ( it ) {
  				auto childNode = it->GetNode();
  				it = it->Next();
  				childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() );
  				parentArrayNode->InsertNodeAtIndex( childNode, contextNode->GetIndex() );
  			}
  		} else {
  			parentArrayNode->InsertNodeAtIndex( parsedNode, contextNode->GetIndex() );
  		}
  
  	}
  
  	static void InsertAfter( const spINode & contextNode, const spINode & parsedNode ) {
  		if ( !contextNode )
  			NOTIFY_ERROR( IError::kEDParser, kPECInvalidContextNode, "Context Node is invalid", IError::kESOperationFatal, false, false );
  		if ( !contextNode->IsArrayItem() )
  			NOTIFY_ERROR( IError::kEDParser, kPECContextNodeParentIsNonArray, "Context Node's Parent is non array node", IError::kESOperationFatal, false, false );
  		pIMetadata meta( NULL );
  		try {
  			meta = parsedNode->GetInterfacePointer< IMetadata >();
  		} catch ( spcIError err ) {
  			meta = NULL;
  		}
  		pIArrayNode parentArrayNode = contextNode->GetINode_I()->GetRawParentPointer()->GetInterfacePointer< IArrayNode >();
  		if ( meta ) {
  			auto it = meta->Iterator();
  			sizet index = contextNode->GetIndex() + 1;
  			while ( it ) {
  				auto childNode = it->GetNode();
  				it = it->Next();
  				childNode = meta->GetIMetadata_I()->RemoveNode( childNode->GetNameSpace(), childNode->GetName() );
  				parentArrayNode->InsertNodeAtIndex( childNode, index );
  				index = childNode->GetIndex() + 1;
  			}
  		} else {
  			parentArrayNode->InsertNodeAtIndex( parsedNode, contextNode->GetIndex() + 1 );
  		}
  	}
  
  	static void ReplaceNode( spINode & node, const spINode & parsedNode ) {
  		if ( node && node->IsArrayItem() && node->GetNodeType() != parsedNode->GetNodeType() ) {
  			NOTIFY_ERROR( IError::kEDDataModel, kDMECArrayItemTypeDifferent, "node type is different than what currently array can hold",
  				IError_v1::kESOperationFatal, true, static_cast< sizet >( node->GetNodeType() ), true, static_cast< sizet >( parsedNode->GetNodeType() ) );
  		}
  		node = parsedNode;
  	}
  
  	void APICALL DOMParserImpl::ParseWithSpecificAction( const char * buffer, sizet bufferLength, eActionType actionType, spINode & node ) {
  		auto parsedNode = ParseAsNode( buffer, bufferLength );
  
  		if ( parsedNode ) {
  			switch ( actionType ) {
  			case kATAppendAsChildren:
  				AppendAsChildren( node, parsedNode );
  				break;
  
  			case kATReplaceChildren:
  				ReplaceChildren( node, parsedNode );
  				break;
  
  			case kATAppendOrReplaceChildren:
  				AppendOrReplaceChildren( node, parsedNode );
  				break;
  
  			case kATInsertBefore:
  				InsertBefore( node, parsedNode );
  				break;
  
  			case kATInsertAfter:
  				InsertAfter( node, parsedNode );
  				break;
  
  			case kATReplace:
  				ReplaceNode( node, parsedNode );
  				break;
  
  			default:
  				NOTIFY_ERROR( IError::kEDGeneral, kGECNotImplemented, "Not yet implemented", IError::kESOperationFatal, true, static_cast< sizet >( actionType ) );
  			}
  
  		}
  	}
  
  	spISharedMutex APICALL DOMParserImpl::GetMutex() const {
  		return mSharedMutex;
  	}
  
  	AdobeXMPCore::spIDOMParser IDOMParser_I::CreateDOMParser( pIClientDOMParser_base clientDOMParser ) {
  		return MakeUncheckedSharedPointer( new ClientDOMParserWrapperImpl( clientDOMParser ), __FILE__, __LINE__, true );
  	}
  
  
  	void DOMParserImpl::SetErrorCallback(XMPMeta::ErrorCallbackInfo * ec) {
  		mGenericErrorCallbackPtr = ec;
  	}
  }
  