/* class static */ void XMPUtils::AppendProperties ( const XMPMeta & source, XMPMeta * dest, XMP_OptionBits options ) { XMP_Assert ( dest != 0 ); // ! Enforced by wrapper. const bool doAll = ((options & kXMPUtil_DoAllProperties) != 0); const bool replaceOld = ((options & kXMPUtil_ReplaceOldValues) != 0); const bool deleteEmpty = ((options & kXMPUtil_DeleteEmptyValues) != 0); for ( size_t schemaNum = 0, schemaLim = source.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) { const XMP_Node * sourceSchema = source.tree.children[schemaNum]; // Make sure we have a destination schema node. Remember if it is newly created. XMP_Node * destSchema = FindSchemaNode ( &dest->tree, sourceSchema->name.c_str(), kXMP_ExistingOnly ); const bool newDestSchema = (destSchema == 0); if ( newDestSchema ) { destSchema = new XMP_Node ( &dest->tree, sourceSchema->name, sourceSchema->value, kXMP_SchemaNode ); dest->tree.children.push_back ( destSchema ); } // Process the source schema's children. Do this backwards in case deleteEmpty is set. for ( long propNum = ((long)sourceSchema->children.size() - 1); propNum >= 0; --propNum ) { const XMP_Node * sourceProp = sourceSchema->children[propNum]; if ( doAll || IsExternalProperty ( sourceSchema->name, sourceProp->name ) ) { AppendSubtree ( sourceProp, destSchema, replaceOld, deleteEmpty ); // *** RemoveMultiValueInfo ( dest, sourceSchema->name.c_str(), sourceProp->name.c_str() ); } } if ( destSchema->children.empty() ) { if ( newDestSchema ) { delete ( destSchema ); dest->tree.children.pop_back(); } else if ( deleteEmpty ) { DeleteEmptySchema ( destSchema ); } } } } // AppendProperties
/* class static */ void XMPUtils::DuplicateSubtree ( const XMPMeta & source, XMPMeta * dest, XMP_StringPtr sourceNS, XMP_StringPtr sourceRoot, XMP_StringPtr destNS, XMP_StringPtr destRoot, XMP_OptionBits options ) { options = options; // Avoid unused parameter warning. bool fullSourceTree = false; bool fullDestTree = false; XMP_ExpandedXPath sourcePath, destPath; const XMP_Node * sourceNode = 0; XMP_Node * destNode = 0; XMP_Assert ( (sourceNS != 0) && (*sourceNS != 0) ); XMP_Assert ( (sourceRoot != 0) && (*sourceRoot != 0) ); XMP_Assert ( (dest != 0) && (destNS != 0) && (destRoot != 0) ); if ( *destNS == 0 ) destNS = sourceNS; if ( *destRoot == 0 ) destRoot = sourceRoot; if ( XMP_LitMatch ( sourceNS, "*" ) ) fullSourceTree = true; if ( XMP_LitMatch ( destNS, "*" ) ) fullDestTree = true; if ( (&source == dest) && (fullSourceTree | fullDestTree) ) { XMP_Throw ( "Can't duplicate tree onto itself", kXMPErr_BadParam ); } if ( fullSourceTree & fullDestTree ) XMP_Throw ( "Use Clone for full tree to full tree", kXMPErr_BadParam ); if ( fullSourceTree ) { // The destination must be an existing empty struct, copy all of the source top level as fields. ExpandXPath ( destNS, destRoot, &destPath ); destNode = FindNode ( &dest->tree, destPath, kXMP_ExistingOnly ); if ( (destNode == 0) || (! XMP_PropIsStruct ( destNode->options )) ) { XMP_Throw ( "Destination must be an existing struct", kXMPErr_BadXPath ); } if ( ! destNode->children.empty() ) { if ( options & kXMP_DeleteExisting ) { destNode->RemoveChildren(); } else { XMP_Throw ( "Destination must be an empty struct", kXMPErr_BadXPath ); } } for ( size_t schemaNum = 0, schemaLim = source.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) { const XMP_Node * currSchema = source.tree.children[schemaNum]; for ( size_t propNum = 0, propLim = currSchema->children.size(); propNum < propLim; ++propNum ) { sourceNode = currSchema->children[propNum]; XMP_Node * copyNode = new XMP_Node ( destNode, sourceNode->name, sourceNode->value, sourceNode->options ); destNode->children.push_back ( copyNode ); CloneOffspring ( sourceNode, copyNode ); } } } else if ( fullDestTree ) { // The source node must be an existing struct, copy all of the fields to the dest top level. XMP_ExpandedXPath sourcePath; ExpandXPath ( sourceNS, sourceRoot, &sourcePath ); sourceNode = FindConstNode ( &source.tree, sourcePath ); if ( (sourceNode == 0) || (! XMP_PropIsStruct ( sourceNode->options )) ) { XMP_Throw ( "Source must be an existing struct", kXMPErr_BadXPath ); } destNode = &dest->tree; if ( ! destNode->children.empty() ) { if ( options & kXMP_DeleteExisting ) { destNode->RemoveChildren(); } else { XMP_Throw ( "Destination tree must be empty", kXMPErr_BadXPath ); } } std::string nsPrefix; XMP_StringPtr nsURI; XMP_StringLen nsLen; for ( size_t fieldNum = 0, fieldLim = sourceNode->children.size(); fieldNum < fieldLim; ++fieldNum ) { const XMP_Node * currField = sourceNode->children[fieldNum]; size_t colonPos = currField->name.find ( ':' ); nsPrefix.assign ( currField->name.c_str(), colonPos ); bool nsOK = XMPMeta::GetNamespaceURI ( nsPrefix.c_str(), &nsURI, &nsLen ); if ( ! nsOK ) XMP_Throw ( "Source field namespace is not global", kXMPErr_BadSchema ); XMP_Node * destSchema = FindSchemaNode ( &dest->tree, nsURI, kXMP_CreateNodes ); if ( destSchema == 0 ) XMP_Throw ( "Failed to find destination schema", kXMPErr_BadSchema ); XMP_Node * copyNode = new XMP_Node ( destSchema, currField->name, currField->value, currField->options ); destSchema->children.push_back ( copyNode ); CloneOffspring ( currField, copyNode ); } } else { // Find the root nodes for the source and destination subtrees. ExpandXPath ( sourceNS, sourceRoot, &sourcePath ); ExpandXPath ( destNS, destRoot, &destPath ); sourceNode = FindConstNode ( &source.tree, sourcePath ); if ( sourceNode == 0 ) XMP_Throw ( "Can't find source subtree", kXMPErr_BadXPath ); destNode = FindNode ( &dest->tree, destPath, kXMP_ExistingOnly ); // Dest must not yet exist. if ( destNode != 0 ) XMP_Throw ( "Destination subtree must not exist", kXMPErr_BadXPath ); destNode = FindNode ( &dest->tree, destPath, kXMP_CreateNodes ); // Now create the dest. if ( destNode == 0 ) XMP_Throw ( "Can't create destination root node", kXMPErr_BadXPath ); // Make sure the destination is not within the source! The source can't be inside the destination // because the source already existed and the destination was just created. if ( &source == dest ) { for ( XMP_Node * testNode = destNode; testNode != 0; testNode = testNode->parent ) { if ( testNode == sourceNode ) { // *** delete the just-created dest root node XMP_Throw ( "Destination subtree is within the source subtree", kXMPErr_BadXPath ); } } } // *** Could use a CloneTree util here and maybe elsewhere. destNode->value = sourceNode->value; // *** Should use SetNode. destNode->options = sourceNode->options; CloneOffspring ( sourceNode, destNode ); } } // DuplicateSubtree
/* class static */ void XMPUtils::RemoveProperties ( XMPMeta * xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options ) { XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // ! Enforced by wrapper. const bool doAll = XMP_TestOption (options, kXMPUtil_DoAllProperties ); const bool includeAliases = XMP_TestOption ( options, kXMPUtil_IncludeAliases ); if ( *propName != 0 ) { // Remove just the one indicated property. This might be an alias, the named schema might // not actually exist. So don't lookup the schema node. if ( *schemaNS == 0 ) XMP_Throw ( "Property name requires schema namespace", kXMPErr_BadParam ); XMP_ExpandedXPath expPath; ExpandXPath ( schemaNS, propName, &expPath ); XMP_NodePtrPos propPos; XMP_Node * propNode = FindNode ( &(xmpObj->tree), expPath, kXMP_ExistingOnly, kXMP_NoOptions, &propPos ); if ( propNode != 0 ) { if ( doAll || IsExternalProperty ( expPath[kSchemaStep].step, expPath[kRootPropStep].step ) ) { XMP_Node * parent = propNode->parent; // *** Should have XMP_Node::RemoveChild(pos). delete propNode; // ! Both delete the node and erase the pointer from the parent. parent->children.erase ( propPos ); DeleteEmptySchema ( parent ); } } } else if ( *schemaNS != 0 ) { // Remove all properties from the named schema. Optionally include aliases, in which case // there might not be an actual schema node. XMP_NodePtrPos schemaPos; XMP_Node * schemaNode = FindSchemaNode ( &xmpObj->tree, schemaNS, kXMP_ExistingOnly, &schemaPos ); if ( schemaNode != 0 ) RemoveSchemaChildren ( schemaPos, doAll ); if ( includeAliases ) { // We're removing the aliases also. Look them up by their namespace prefix. Yes, the // alias map is sorted so we could process just that portion. But that takes more code // and the extra speed isn't worth it. (Plus this way we avoid a dependence on the map // implementation.) Lookup the XMP node from the alias, to make sure the actual exists. XMP_StringPtr nsPrefix; XMP_StringLen nsLen; (void) XMPMeta::GetNamespacePrefix ( schemaNS, &nsPrefix, &nsLen ); XMP_AliasMapPos currAlias = sRegisteredAliasMap->begin(); XMP_AliasMapPos endAlias = sRegisteredAliasMap->end(); for ( ; currAlias != endAlias; ++currAlias ) { if ( strncmp ( currAlias->first.c_str(), nsPrefix, nsLen ) == 0 ) { XMP_NodePtrPos actualPos; XMP_Node * actualProp = FindNode ( &xmpObj->tree, currAlias->second, kXMP_ExistingOnly, kXMP_NoOptions, &actualPos ); if ( actualProp != 0 ) { XMP_Node * rootProp = actualProp; while ( ! XMP_NodeIsSchema ( rootProp->parent->options ) ) rootProp = rootProp->parent; if ( doAll || IsExternalProperty ( rootProp->parent->name, rootProp->name ) ) { XMP_Node * parent = actualProp->parent; delete actualProp; // ! Both delete the node and erase the pointer from the parent. parent->children.erase ( actualPos ); DeleteEmptySchema ( parent ); } } } } } } else { // Remove all appropriate properties from all schema. In this case we don't have to be // concerned with aliases, they are handled implicitly from the actual properties. // ! Iterate backwards to reduce shuffling if schema are erased and to simplify the logic // ! for denoting the current schema. (Erasing schema n makes the old n+1 now be n.) size_t schemaCount = xmpObj->tree.children.size(); XMP_NodePtrPos beginPos = xmpObj->tree.children.begin(); for ( size_t schemaNum = schemaCount-1, schemaLim = (size_t)(-1); schemaNum != schemaLim; --schemaNum ) { XMP_NodePtrPos currSchema = beginPos + schemaNum; RemoveSchemaChildren ( currSchema, doAll ); } } } // RemoveProperties
static XMP_Node * AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel ) { #if 0 cout << "AddChildNode, parent = " << xmpParent->name << ", child = " << xmlNode.name; cout << ", value = \"" << value << '"'; if ( isTopLevel ) cout << ", top level"; cout << endl; #endif if ( xmlNode.ns.empty() ) { XMP_Throw ( "XML namespace required for all elements and attributes", kXMPErr_BadRDF ); } XMP_StringPtr childName = xmlNode.name.c_str(); const bool isArrayItem = (xmlNode.name == "rdf:li"); const bool isValueNode = (xmlNode.name == "rdf:value"); XMP_OptionBits childOptions = 0; if ( isTopLevel ) { // Lookup the schema node, adjust the XMP parent pointer. XMP_Assert ( xmpParent->parent == 0 ); // Incoming parent must be the tree root. XMP_Node * schemaNode = FindSchemaNode ( xmpParent, xmlNode.ns.c_str(), kXMP_CreateNodes ); if ( schemaNode->options & kXMP_NewImplicitNode ) schemaNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. // *** Should use "opt &= ~flag" (no conditional), need runtime check for proper 32 bit code. xmpParent = schemaNode; // If this is an alias set the isAlias flag in the node and the hasAliases flag in the tree. if ( sRegisteredAliasMap->find ( xmlNode.name ) != sRegisteredAliasMap->end() ) { childOptions |= kXMP_PropIsAlias; schemaNode->parent->options |= kXMP_PropHasAliases; } } // Make sure that this is not a duplicate of a named node. if ( ! (isArrayItem | isValueNode) ) { if ( FindChildNode ( xmpParent, childName, kXMP_ExistingOnly ) != 0 ) { XMP_Throw ( "Duplicate property or field node", kXMPErr_BadXMP ); } } // Add the new child to the XMP parent node. XMP_Node * newChild = new XMP_Node ( xmpParent, childName, value, childOptions ); if ( (! isValueNode) || xmpParent->children.empty() ) { xmpParent->children.push_back ( newChild ); } else { xmpParent->children.insert ( xmpParent->children.begin(), newChild ); } if ( isValueNode ) { if ( isTopLevel || (! (xmpParent->options & kXMP_PropValueIsStruct)) ) XMP_Throw ( "Misplaced rdf:value element", kXMPErr_BadRDF ); xmpParent->options |= kRDF_HasValueElem; } if ( isArrayItem ) { if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) XMP_Throw ( "Misplaced rdf:li element", kXMPErr_BadRDF ); newChild->name = kXMP_ArrayItemName; #if 0 // *** XMP_DebugBuild newChild->_namePtr = newChild->name.c_str(); #endif } return newChild; } // AddChildNode