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; } }
extern void ComposeXPath ( const XMP_ExpandedXPath & expandedXPath, XMP_VarString * stringXPath ) { *stringXPath = expandedXPath[kRootPropStep].step; for ( size_t index = kRootPropStep+1; index < expandedXPath.size(); ++index ) { const XPathStepInfo & currStep = expandedXPath[index]; switch ( currStep.options & kXMP_StepKindMask ) { case kXMP_StructFieldStep : case kXMP_QualifierStep : *stringXPath += '/'; *stringXPath += currStep.step; break; case kXMP_ArrayIndexStep : case kXMP_ArrayLastStep : case kXMP_QualSelectorStep : case kXMP_FieldSelectorStep : *stringXPath += currStep.step; break; default: XMP_Throw ( "Unexpected", kXMPErr_InternalFailure ); } } } // ComposeXPath
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; }
XMPIterator::XMPIterator ( const XMPMeta & xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options ) : clientRefs(0), info(IterInfo(options,&xmpObj)) { if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) { XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions ); } // *** Lock the XMPMeta object if we ever stop using a full DLL lock. if ( *propName != 0 ) { // An iterator rooted at a specific node. #if TraceIterators printf ( "\nNew XMP property iterator for \"%s\", options = %X\n Schema = %s, root = %s\n", xmpObj.tree.name.c_str(), options, schemaNS, propName ); #endif XMP_ExpandedXPath propPath; ExpandXPath ( schemaNS, propName, &propPath ); XMP_Node * propNode = FindConstNode ( &xmpObj.tree, propPath ); // If not found get empty iteration. if ( propNode != 0 ) { XMP_VarString rootName ( propPath[1].step ); // The schema is [0]. for ( size_t i = 2; i < propPath.size(); ++i ) { XMP_OptionBits stepKind = GetStepKind ( propPath[i].options ); if ( stepKind <= kXMP_QualifierStep ) rootName += '/'; rootName += propPath[i].step; } propName = rootName.c_str(); size_t leafOffset = rootName.size(); while ( (leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[') ) --leafOffset; if ( propName[leafOffset] == '/' ) ++leafOffset; info.tree.children.push_back ( IterNode ( propNode->options, propName, leafOffset ) ); SetCurrSchema ( info, propPath[kSchemaStep].step.c_str() ); if ( info.options & kXMP_IterJustChildren ) { AddNodeOffspring ( info, info.tree.children.back(), propNode ); } } } else if ( *schemaNS != 0 ) { // An iterator for all properties in one schema. #if TraceIterators printf ( "\nNew XMP schema iterator for \"%s\", options = %X\n Schema = %s\n", xmpObj.tree.name.c_str(), options, schemaNS ); #endif info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaNS, 0 ) ); IterNode & iterSchema = info.tree.children.back(); XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS ); if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema ); if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, schemaNS ); if ( iterSchema.children.empty() ) { info.tree.children.pop_back(); // No properties, remove the schema node. } else { SetCurrSchema ( info, schemaNS ); } } else { // An iterator for all properties in all schema. First add schema that exist (have children), // adding aliases from them if appropriate. Then add schema that have no actual properties // but do have aliases to existing properties, if we're including aliases in the iteration. #if TraceIterators printf ( "\nNew XMP tree iterator for \"%s\", options = %X\n", xmpObj.tree.name.c_str(), options ); #endif // First pick up the schema that exist. for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) { const XMP_Node * xmpSchema = xmpObj.tree.children[schemaNum]; info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, xmpSchema->name, 0 ) ); IterNode & iterSchema = info.tree.children.back(); if ( ! (info.options & kXMP_IterJustChildren) ) { AddSchemaProps ( info, iterSchema, xmpSchema ); if ( info.options & kXMP_IterIncludeAliases ) AddSchemaAliases ( info, iterSchema, xmpSchema->name.c_str() ); if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node. } } if ( info.options & kXMP_IterIncludeAliases ) { // Add the schema that only have aliases. The most convenient, and safest way, is to go // through the registered namespaces, see if it exists, and let AddSchemaAliases do its // thing if not. Don't combine with the above loop, it is nicer to have the "real" stuff // be in storage order (not subject to the namespace map order). // ! We don't do the kXMP_IterJustChildren handing in the same way here as above. The // ! existing schema (presumably) have actual children. We need to call AddSchemaAliases // ! here to determine if the namespace has any aliases to existing properties. We then // ! strip the children if necessary. XMP_cStringMapPos currNS = sNamespaceURIToPrefixMap->begin(); XMP_cStringMapPos endNS = sNamespaceURIToPrefixMap->end(); for ( ; currNS != endNS; ++currNS ) { XMP_StringPtr schemaName = currNS->first.c_str(); if ( FindConstSchema ( &xmpObj.tree, schemaName ) != 0 ) continue; info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaName, 0 ) ); IterNode & iterSchema = info.tree.children.back(); AddSchemaAliases ( info, iterSchema, schemaName ); if ( iterSchema.children.empty() ) { info.tree.children.pop_back(); // No aliases, remove the schema node. } else if ( info.options & kXMP_IterJustChildren ) { iterSchema.children.clear(); // Get rid of the children. } } } } // Set the current iteration position to the first node to be visited. info.currPos = info.tree.children.begin(); info.endPos = info.tree.children.end(); if ( (info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0) ) { info.currPos->visitStage = kIter_VisitSelf; } #if TraceIterators if ( info.currPos == info.endPos ) { printf ( " ** Empty iteration **\n" ); } else { printf ( " Initial node %s, stage = %s, iterator @ %.8X\n", info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); } #endif } // XMPIterator for XMPMeta objects
XMPIterator2::XMPIterator2 ( const XMPMeta & xmpObjBase, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options) :XMPIterator(xmpObjBase, schemaNS, propName, options) { using namespace AdobeXMPCore; if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) { XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions ); } iteratorOptions = options; info.options = options; if(sUseNewCoreAPIs) { const XMPMeta2 & tempPtr = dynamic_cast<const XMPMeta2 &>(xmpObjBase); } else { XMP_Throw("Unsupported iteration kind", kXMPErr_BadOptions); } const XMPMeta2 & xmpObj = dynamic_cast<const XMPMeta2 &>(xmpObjBase); spIMetadata root = xmpObj.mDOM; mDOM = xmpObj.mDOM; // *** Lock the XMPMeta object if we ever stop using a full DLL lock. if ( *propName != 0 ) { XMP_ExpandedXPath propPath; ExpandXPath(schemaNS, propName, &propPath); spINode destNode; XMP_OptionBits destOptions = 0; bool nodeFound = XMPUtils::FindCnstNode(root, propPath, destNode,&destOptions); if (nodeFound) { XMP_VarString rootName(propPath[1].step); // The schema is [0]. for (size_t i = 2; i < propPath.size(); ++i) { XMP_OptionBits stepKind = GetStepKind(propPath[i].options); if (stepKind <= kXMP_QualifierStep) rootName += '/'; rootName += propPath[i].step; } propName = rootName.c_str(); size_t leafOffset = rootName.size(); while ((leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[')) --leafOffset; if (propName[leafOffset] == '/') ++leafOffset; info.tree.nodeChildren.push_back( IteratorNode( destOptions, propName, leafOffset ) ); SetCurrentSchema(propPath[kSchemaStep].step.c_str()); if (options & kXMP_IterJustChildren) { AddNodeOffSpring(info.tree.nodeChildren.back(), destNode); } } } else if ( *schemaNS != 0 ) { info.tree.nodeChildren.push_back(IteratorNode(kXMP_SchemaNode, schemaNS, 0)); IteratorNode & iterSchema = info.tree.nodeChildren.back(); bool schemaFound = false; for (auto childIter = mDOM->Iterator(); childIter; childIter = childIter->Next()) { if (!strcmp(childIter->GetNode()->GetNameSpace()->c_str(), schemaNS)) { schemaFound = true; break; } } if (schemaFound) AddSchemaProperties(iterSchema, schemaNS); if (iterSchema.nodeChildren.empty()) { info.tree.nodeChildren.pop_back(); // No properties, remove the schema node. } else { SetCurrentSchema(schemaNS); } } else { std::map < XMP_VarString, bool > schemaProperties; for (auto childIter = mDOM->Iterator(); childIter; childIter = childIter->Next()) { spINode childNode = childIter->GetNode(); schemaProperties[childNode->GetNameSpace()->c_str()] = true; } for (auto key : schemaProperties) { //TODO check name info.tree.nodeChildren.push_back(IteratorNode( kXMP_SchemaNode, key.first, 0 )); IteratorNode & iterSchema = info.tree.nodeChildren.back(); if (!(info.options & kXMP_IterJustChildren)) { AddSchemaProperties(iterSchema, key.first.c_str()); // if (iterSchema.nodeChildren.empty()) info.tree.nodeChildren.pop_back(); // No properties, remove the schema node. } } } info.currPos = info.tree.nodeChildren.begin(); info.endPos = info.tree.nodeChildren.end(); if ((info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0)) { info.currPos->visitStage = kIter_VisitSelf; } } // XMPIterator for XMPMeta objects
XMPIterator::XMPIterator ( const XMPMeta & xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options ) : clientRefs(0), info(IterInfo(options,&xmpObj)) { if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) { XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions ); } // *** Lock the XMPMeta object if we ever stop using a full DLL lock. if ( *propName != 0 ) { // An iterator rooted at a specific node. #if TraceIterators printf ( "\nNew XMP property iterator for \"%s\", options = %X\n Schema = %s, root = %s\n", xmpObj.tree.name.c_str(), options, schemaNS, propName ); #endif XMP_ExpandedXPath propPath; ExpandXPath ( schemaNS, propName, &propPath ); XMP_Node * propNode = FindConstNode ( &xmpObj.tree, propPath ); // If not found get empty iteration. if ( propNode != 0 ) { XMP_VarString rootName ( propPath[1].step ); // The schema is [0]. for ( size_t i = 2; i < propPath.size(); ++i ) { XMP_OptionBits stepKind = GetStepKind ( propPath[i].options ); if ( stepKind <= kXMP_QualifierStep ) rootName += '/'; rootName += propPath[i].step; } propName = rootName.c_str(); size_t leafOffset = rootName.size(); while ( (leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[') ) --leafOffset; if ( propName[leafOffset] == '/' ) ++leafOffset; info.tree.children.push_back ( IterNode ( propNode->options, propName, leafOffset ) ); SetCurrSchema ( info, propPath[kSchemaStep].step.c_str() ); if ( info.options & kXMP_IterJustChildren ) { AddNodeOffspring ( info, info.tree.children.back(), propNode ); } } } else if ( *schemaNS != 0 ) { // An iterator for all properties in one schema. #if TraceIterators printf ( "\nNew XMP schema iterator for \"%s\", options = %X\n Schema = %s\n", xmpObj.tree.name.c_str(), options, schemaNS ); #endif info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaNS, 0 ) ); IterNode & iterSchema = info.tree.children.back(); XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS ); if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema ); if ( iterSchema.children.empty() ) { info.tree.children.pop_back(); // No properties, remove the schema node. } else { SetCurrSchema ( info, schemaNS ); } } else { // An iterator for all properties in all schema. First add schema that exist (have children), // adding aliases from them if appropriate. Then add schema that have no actual properties // but do have aliases to existing properties, if we're including aliases in the iteration. #if TraceIterators printf ( "\nNew XMP tree iterator for \"%s\", options = %X\n", xmpObj.tree.name.c_str(), options ); #endif // First pick up the schema that exist. for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) { const XMP_Node * xmpSchema = xmpObj.tree.children[schemaNum]; info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, xmpSchema->name, 0 ) ); IterNode & iterSchema = info.tree.children.back(); if ( ! (info.options & kXMP_IterJustChildren) ) { AddSchemaProps ( info, iterSchema, xmpSchema ); if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node. } } } // Set the current iteration position to the first node to be visited. info.currPos = info.tree.children.begin(); info.endPos = info.tree.children.end(); if ( (info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0) ) { info.currPos->visitStage = kIter_VisitSelf; } #if TraceIterators if ( info.currPos == info.endPos ) { printf ( " ** Empty iteration **\n" ); } else { printf ( " Initial node %s, stage = %s, iterator @ %.8X\n", info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); } #endif } // XMPIterator for XMPMeta objects