// ================================================================================================= // Copyright Adobe // Copyright 2020 Adobe // 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/MetadataImpl.h" #undef IMPLEMENTATION_HEADERS_CAN_BE_INCLUDED #include "XMPCommon/Utilities/UTF8String.h" #include "XMPCommon/Interfaces/IUTF8String_I.h" #include "XMPCommon/Utilities/AutoSharedLock.h" #include "XMPCommon/Utilities/TSmartPointers_I.h" #include "XMPCommon/Interfaces/IError_I.h" #include "XMPCore/XMPCoreErrorCodes.h" #include "XMPCore/Interfaces/INameSpacePrefixMap_I.h" #include "XMPCore/Interfaces/IArrayNode_I.h" #include "XMPCore/Interfaces/INodeIterator_I.h" #include "XMPCore/Interfaces/ISimpleNode_I.h" #include "XMPCore/source/XMPCore_Impl.hpp" #include namespace AdobeXMPCore_Int { static const char * kMetadataNameSpace( "http://metadata" ); static const AdobeXMPCommon::sizet kMetadataNameSpaceLength( 15 ); static const char * kMetadataLocalName( "_metadata_" ); static const AdobeXMPCommon::sizet kMetadataLocalNameLength( 10 ); MetadataImpl::MetadataImpl() : StructureNodeImpl( kMetadataNameSpace, kMetadataNameSpaceLength, kMetadataLocalName, kMetadataLocalNameLength ) , NodeImpl( kMetadataNameSpace, kMetadataNameSpaceLength, kMetadataLocalName, kMetadataLocalNameLength ) , mAboutURI( IUTF8String_I::CreateUTF8String() ) , mSupportAliases( false ) { } spcIUTF8String APICALL MetadataImpl::GetAboutURI() const { AutoSharedLock lock( mSharedMutex ); return mAboutURI; } void APICALL MetadataImpl::SetAboutURI( const char * uri, sizet uriLength ) __NOTHROW__ { AutoSharedLock lock( mSharedMutex, true ); mAboutURI->assign( uri, uriLength ); } spcIUTF8String APICALL MetadataImpl::GetName() const { return IUTF8String_I::CreateUTF8String(); } spcIUTF8String APICALL MetadataImpl::GetNameSpace() const { return IUTF8String_I::CreateUTF8String(); } spINode APICALL MetadataImpl::GetParent() { return spINode(); } INode::eNodeType APICALL MetadataImpl::GetParentNodeType() const { return INode::kNTNone; } void APICALL MetadataImpl::SetName( const char * name, sizet nameLength ) { return; } void APICALL MetadataImpl::SetNameSpace( const char * nameSpace, sizet nameSpaceLength ) { return; } void APICALL MetadataImpl::ClearContents() { StructureNodeImpl::ClearContents(); { AutoSharedLock lock( mSharedMutex, true ); mAboutURI->clear(); } } spINode APICALL MetadataImpl::CloneContents( bool ignoreEmptyNodes, bool ignoreNodesWithOnlyQualifiers, sizet qualifiersCount ) const { spIMetadata newNode = IMetadata::CreateMetadata(); auto endIt = mChildrenMap.end(); for ( auto it = mChildrenMap.begin(); it != endIt; ++it ) { spINode childNode = it->second->Clone( ignoreEmptyNodes, ignoreNodesWithOnlyQualifiers ); if ( childNode ) { newNode->AppendNode( childNode ); } } newNode->SetAboutURI( mAboutURI->c_str(), mAboutURI->size() ); return newNode; } spIMetadata APICALL MetadataImpl::ConvertToMetadata() { return MakeUncheckedSharedPointer( this, __FILE__, __LINE__ ); } void APICALL MetadataImpl::EnableFeature( const char * key, sizet keyLength ) const __NOTHROW__ { UTF8String keyStr( key, keyLength ); if ( keyStr.compare( "alias" ) == 0 ) mSupportAliases = true; } void APICALL MetadataImpl::DisableFeature( const char * key, sizet keyLength ) const __NOTHROW__ { UTF8String keyStr( key, keyLength ); if ( keyStr.compare( "alias" ) == 0 ) mSupportAliases = false; } bool IsNodeAlias( const char * nameSpace, const char * name, XMP_ExpandedXPath & exPath ); bool IsNodeAlias( const char * nameSpace, const char * name, XMP_ExpandedXPath & exPath ) { spIUTF8String qualName = IUTF8String_I::CreateUTF8String(); auto defaultMap = INameSpacePrefixMap::GetDefaultNameSpacePrefixMap(); spcIUTF8String prefixStr = defaultMap->GetPrefix( nameSpace, AdobeXMPCommon::npos ); if ( !prefixStr ) { return false; } qualName->append( prefixStr )->append( ":", AdobeXMPCommon::npos )->append( name, AdobeXMPCommon::npos ); bool aliasFound = sRegisteredAliasMap->count( qualName->c_str() ) > 0; if ( aliasFound ) { exPath = ( *sRegisteredAliasMap )[ qualName->c_str() ]; return aliasFound; } return aliasFound; } bool HandleConstAlias( const spIMetadata & meta, spINode & destNode, const XMP_ExpandedXPath & expandedXPath, sizet & nodeIndex ); bool HandleConstAlias( const spIMetadata & meta, spINode & destNode, const XMP_ExpandedXPath & expandedXPath, sizet & nodeIndex ) { if ( expandedXPath.empty() ) NOTIFY_ERROR( IError::kEDGeneral, kGECLogicalError, "Empty XPath", IError::kESOperationFatal, false, false ); if ( !( expandedXPath[ kSchemaStep ].options & kXMP_SchemaNode ) ) { return false; } else { XMP_VarString namespaceName = expandedXPath[ kSchemaStep ].step.c_str(); size_t colonPos = expandedXPath[ kRootPropStep ].step.find( ":" ); assert( colonPos != std::string::npos ); XMP_VarString propertyName = expandedXPath[ kRootPropStep ].step.substr( colonPos + 1 ); // here find the node with this name destNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !destNode ) return false; if ( expandedXPath.size() == 2 ) return true; assert( destNode->GetNodeType() == INode::kNTArray ); if ( expandedXPath[ 2 ].options == kXMP_ArrayIndexStep ) { assert( expandedXPath[ 2 ].step == "[1]" ); destNode = destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ); auto actualNodeType = destNode->GetNodeType(); if ( destNode ) { if ( nodeIndex ) nodeIndex = 1; return true; } return false; } else if ( expandedXPath[ 2 ].options == kXMP_QualSelectorStep ) { assert( expandedXPath[ 2 ].step == "[?xml:lang=\"x-default\"]" ); if ( !destNode || destNode->GetNodeType() != INode::kNTArray ) return false; spINodeIterator iter = destNode->ConvertToArrayNode()->Iterator(); sizet index = 1; while ( iter ) { spINode node = iter->GetNode(); try { spISimpleNode qualNode = node->GetSimpleQualifier( "http://www.w3.org/XML/1998/namespace", AdobeXMPCommon::npos, "lang", AdobeXMPCommon::npos ); if ( qualNode->GetValue()->compare( "x-default" ) == 0 ) { destNode = node; if ( nodeIndex ) nodeIndex = index; return true; } } catch ( spcIError err ) { } catch ( ... ) {} index++; iter = iter->Next(); } return false; } return false; } } static spINode CreateTerminalNode( const char* nameSpace, const char * name, XMP_OptionBits options, const spcINode & nodeToBeCloned = spINode() ) { spINode newNode; if ( nodeToBeCloned ) { newNode = nodeToBeCloned->Clone(); } else if ( XMP_PropIsSimple( options ) ) { newNode = ISimpleNode::CreateSimpleNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); } else if ( XMP_PropIsStruct( options ) ) { newNode = IStructureNode_v1::CreateStructureNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); } else if ( XMP_PropIsArray( options ) ) { if ( options & kXMP_PropArrayIsAltText ) newNode = IArrayNode_v1::CreateAlternativeArrayNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); else if ( options & kXMP_PropArrayIsOrdered ) newNode = IArrayNode_v1::CreateOrderedArrayNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); else newNode = IArrayNode_v1::CreateUnorderedArrayNode( nameSpace, AdobeXMPCommon::npos, name, AdobeXMPCommon::npos ); } return newNode; } static bool HandleNonConstAlias( const spIMetadata & meta, XMP_ExpandedXPath & expandedXPath, bool createNodes, XMP_OptionBits leafOptions, spINode & destNode, sizet & nodeIndex, bool ignoreLastStep, const spINode & inputNode ) { destNode = meta; spcIUTF8String inputNodeValue; if ( inputNode && inputNode->GetNodeType() == INode::kNTSimple ) { inputNodeValue = inputNode->ConvertToSimpleNode()->GetValue(); } bool isAliasBeingCreated = expandedXPath.size() == 2; if ( expandedXPath.empty() ) NOTIFY_ERROR( IError::kEDDataModel, kDMECBadXPath, "Empty XPath", IError::kESOperationFatal, false, false ); if ( !( expandedXPath[ kSchemaStep ].options & kXMP_SchemaNode ) ) { return false; } else { XMP_VarString namespaceName = expandedXPath[ kSchemaStep ].step.c_str(); size_t colonPos = expandedXPath[ kRootPropStep ].step.find( ":" ); assert( colonPos != std::string::npos ); XMP_VarString propertyName = expandedXPath[ kRootPropStep ].step.substr( colonPos + 1 ); spcINode childNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !childNode && !createNodes ) return false; if ( expandedXPath.size() == 2 ) { if ( childNode ) return true; XMP_OptionBits createOptions = 0; spINode tempNode; if ( isAliasBeingCreated ) tempNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), leafOptions ); else tempNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), createOptions ); if ( !tempNode ) return false; if ( inputNodeValue ) tempNode->ConvertToSimpleNode()->SetValue( inputNodeValue->c_str(), inputNodeValue->size() ); if ( destNode == meta ) { meta->InsertNode( tempNode ); } else { destNode->ConvertToStructureNode()->AppendNode( tempNode ); } destNode = tempNode; if ( destNode ) return true; return false; } XMP_Assert( expandedXPath.size() == 3 ); if ( expandedXPath[ 2 ].options == kXMP_ArrayIndexStep ) { XMP_Assert( expandedXPath[ 2 ].step == "[1]" ); destNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !destNode && !createNodes ) return false; if ( !destNode ) { spINode arrayNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), kXMP_PropArrayIsOrdered | kXMP_PropValueIsArray ); meta->AppendNode( arrayNode ); destNode = arrayNode; } if ( destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ) ) { destNode = destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ); if ( nodeIndex ) nodeIndex = 1; return true; } else { spISimpleNode indexNode = ISimpleNode::CreateSimpleNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( inputNodeValue ) { indexNode->SetValue( inputNodeValue->c_str(), inputNodeValue->size() ); } destNode->ConvertToArrayNode()->InsertNodeAtIndex( indexNode, 1 ); destNode = indexNode; return true; } return false; } else if ( expandedXPath[ 2 ].options == kXMP_QualSelectorStep ) { assert( expandedXPath[ 2 ].step == "[?xml:lang=\"x-default\"]" ); destNode = meta->GetNode( namespaceName.c_str(), namespaceName.size(), propertyName.c_str(), propertyName.size() ); if ( !destNode && !createNodes ) return false; spINode arrayNode = CreateTerminalNode( namespaceName.c_str(), propertyName.c_str(), kXMP_PropValueIsArray | kXMP_PropArrayIsAltText); meta->AppendNode( arrayNode ); destNode = arrayNode; auto iter = destNode->ConvertToArrayNode()->Iterator(); XMP_Index index = 1; while ( iter ) { spINode node = iter->GetNode(); spINode qualNode = node->GetQualifier( "http://www.w3.org/XML/1998/namespace", AdobeXMPCommon::npos, "lang", AdobeXMPCommon::npos ); if ( qualNode->GetNodeType() == INode::kNTSimple ) { if ( !qualNode->ConvertToSimpleNode()->GetValue()->compare( "x-default" ) ) { destNode = node; if ( nodeIndex ) nodeIndex = index; return true; } } index++; iter = iter->Next(); } spISimpleNode qualifierNode = ISimpleNode::CreateSimpleNode( "http://www.w3.org/XML/1998/namespace", AdobeXMPCommon::npos, "lang", AdobeXMPCommon::npos, "x-default", AdobeXMPCommon::npos ); if ( destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ) ) { destNode = destNode->ConvertToArrayNode()->GetNodeAtIndex( 1 ); if ( nodeIndex ) nodeIndex = 1; destNode->InsertQualifier( qualifierNode ); return true; } else { spISimpleNode indexNode = ISimpleNode::CreateSimpleNode( namespaceName.c_str(), AdobeXMPCommon::npos, propertyName.c_str(), AdobeXMPCommon::npos ); if ( inputNodeValue ) { indexNode->SetValue( inputNodeValue->c_str(), inputNodeValue->size() ); } destNode->ConvertToArrayNode()->InsertNodeAtIndex( indexNode, 1 ); destNode->InsertQualifier( qualifierNode ); destNode = indexNode; return true; } } } return false; } spINode APICALL MetadataImpl::ReplaceNode( const spINode & node ) { if ( mSupportAliases ) { XMP_ExpandedXPath exPath; QualifiedName qName( node->GetNameSpace(), node->GetName() ); bool nodeIsAlias = IsNodeAlias( node->GetNameSpace()->c_str(), node->GetName()->c_str(), exPath ); if ( nodeIsAlias ) { spINode actualNodeToBeRemoved; sizet nodeIndex = 0; auto spSelf = MakeUncheckedSharedPointer( this, __FILE__, __LINE__, false ); if ( HandleConstAlias( spSelf, actualNodeToBeRemoved, exPath, nodeIndex ) ) { qName = QualifiedName( actualNodeToBeRemoved->GetNameSpace(), actualNodeToBeRemoved->GetName() ); } } if ( CheckSuitabilityToBeUsedAsChildNode( node ) && GetNode( qName.mNameSpace, qName.mName ) ) { auto retValue = RemoveNode( qName.mNameSpace, qName.mName ); spINode destNode = node; if ( nodeIsAlias ) { sizet destNodeIndex = 0; auto spSelf = MakeUncheckedSharedPointer( this, __FILE__, __LINE__, false ); if ( !HandleNonConstAlias( spSelf, exPath, true, 0, destNode, destNodeIndex, false, node ) ) { return destNode; } } InsertNode( destNode ); return retValue; } else { NOTIFY_ERROR( IError_v1::kEDDataModel, kDMECNoSuchNodeExists, "no such node exists with the specified qualified name", IError_v1::kESOperationFatal, true, node->GetNameSpace(), true, node->GetName() ); } return spINode(); } else { return StructureNodeImpl::ReplaceNode( node ); } } void APICALL MetadataImpl::InsertNode( const spINode & node ) { if ( mSupportAliases ) { if ( !node ) return; XMP_ExpandedXPath exPath; if ( IsNodeAlias( node->GetNameSpace()->c_str(), node->GetName()->c_str(), exPath ) ) { spINode destNode; sizet destNodeIndex = 0; AutoSharedLock lock( mSharedMutex, true ); auto spSelf = MakeUncheckedSharedPointer(this, __FILE__, __LINE__, false); if ( HandleNonConstAlias( spSelf, exPath, true, 0, destNode, destNodeIndex, false, node ) ) { } } else { return StructureNodeImpl::InsertNode( node ); } } else { return StructureNodeImpl::InsertNode( node ); } return; } spINode APICALL MetadataImpl::RemoveNode( const spcIUTF8String & nameSpace, const spcIUTF8String & name ) { if ( mSupportAliases ) { XMP_ExpandedXPath exPath; if (IsNodeAlias(nameSpace->c_str(), name->c_str(), exPath)) { spINode destNode; sizet destNodeIndex = 0; auto spSelf = MakeUncheckedSharedPointer(this, __FILE__, __LINE__, false); AutoSharedLock lock(mSharedMutex, true); /*if (!HandleConstAlias(spSelf, destNode, exPath, destNodeIndex)) { destNode = spINode(); } return destNode;*/ if (!HandleConstAlias(spSelf, destNode, exPath, destNodeIndex)) { destNode = spINode(); return destNode; } else { return StructureNodeImpl::RemoveNode(destNode->GetNameSpace(), destNode->GetName()); } } else { //return StructureNodeImpl::GetNode(nameSpace, name); return StructureNodeImpl::RemoveNode(nameSpace, name); } } else { return StructureNodeImpl::RemoveNode( nameSpace, name ); } } spINode APICALL MetadataImpl::GetNode( const spcIUTF8String & nameSpace, const spcIUTF8String & name ) { if ( mSupportAliases ) { XMP_ExpandedXPath exPath; if (IsNodeAlias(nameSpace->c_str(), name->c_str(), exPath)) { spINode destNode; sizet destNodeIndex = 0; AutoSharedLock lock(mSharedMutex, true); auto spSelf = MakeUncheckedSharedPointer(this, __FILE__, __LINE__, false); if (!HandleConstAlias(spSelf, destNode, exPath, destNodeIndex)) { destNode = spINode(); } return destNode; } else { return StructureNodeImpl::GetNode(nameSpace, name); } } else { return StructureNodeImpl::GetNode( nameSpace, name ); } } } #if BUILDING_XMPCORE_LIB || SOURCE_COMPILING_XMPCORE_LIB namespace AdobeXMPCore { using namespace AdobeXMPCore_Int; spIMetadata IMetadata::CreateMetadata() { return MakeUncheckedSharedPointer( new MetadataImpl(), __FILE__, __LINE__, true ); } spIMetadata IMetadata_v1::MakeShared( pIMetadata_base ptr ) { if ( !ptr ) return spIMetadata(); pIMetadata p = IMetadata::GetInterfaceVersion() > 1 ? ptr->GetInterfacePointer< IMetadata >() : ptr; return XMP_COMPONENT_INT_NAMESPACE::MakeUncheckedSharedPointer( p, __FILE__, __LINE__, true ); } } #endif // BUILDING_XMPCORE_LIB || SOURCE_COMPILING_XMPCORE_LIB