static void DeclareOneNamespace ( XMP_StringPtr nsPrefix, XMP_StringPtr nsURI, XMP_VarString & usedNS, // ! A catenation of the prefixes with colons. XMP_VarString & outputStr, XMP_StringPtr newline, XMP_StringPtr indentStr, XMP_Index indent ) { XMP_VarString boundedPrefix = ":"; boundedPrefix += nsPrefix; size_t nsPos = usedNS.find ( boundedPrefix ); if ( nsPos == XMP_VarString::npos ) { outputStr += newline; for ( ; indent > 0; --indent ) outputStr += indentStr; outputStr += "xmlns:"; outputStr += nsPrefix; if (outputStr[outputStr.size ( ) - 1] == ':') outputStr[outputStr.size ( ) - 1] = '='; // Change the colon to =. else outputStr += '='; outputStr += '"'; outputStr += nsURI; outputStr += '"'; usedNS += nsPrefix; } } // DeclareOneNamespace
bool XMP_NamespaceTable::Define ( XMP_StringPtr _uri, XMP_StringPtr _suggPrefix, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) { XMP_AutoLock tableLock ( &this->lock, kXMP_WriteLock ); bool prefixMatches = false; XMP_Assert ( (_uri != 0) && (*_uri != 0) && (_suggPrefix != 0) && (*_suggPrefix != 0) ); XMP_VarString uri ( _uri ); XMP_VarString suggPrefix ( _suggPrefix ); if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':'; VerifySimpleXMLName ( _suggPrefix, _suggPrefix+suggPrefix.size()-1 ); // Exclude the colon. XMP_StringMapPos uriPos = this->uriToPrefixMap.find ( uri ); if ( uriPos == this->uriToPrefixMap.end() ) { // The URI is not yet registered, make sure we use a unique prefix. XMP_VarString uniqPrefix ( suggPrefix ); int suffix = 0; char buffer [32]; // AUDIT: Plenty of room for the "_%d_" suffix. while ( true ) { if ( this->prefixToURIMap.find ( uniqPrefix ) == this->prefixToURIMap.end() ) break; ++suffix; snprintf ( buffer, sizeof(buffer), "_%d_:", suffix ); // AUDIT: Using sizeof for snprintf length is safe. uniqPrefix = suggPrefix; uniqPrefix.erase ( uniqPrefix.size()-1 ); // ! Remove the trailing ':'. uniqPrefix += buffer; } // Add the new namespace to both maps. XMP_StringPair newNS ( uri, uniqPrefix ); uriPos = this->uriToPrefixMap.insert ( this->uriToPrefixMap.end(), newNS ); newNS.first.swap ( newNS.second ); (void) this->prefixToURIMap.insert ( this->prefixToURIMap.end(), newNS ); } // Return the actual prefix and see if it matches the suggested prefix. if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str(); if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size(); prefixMatches = ( uriPos->second == suggPrefix ); return prefixMatches; } // XMP_NamespaceTable::Define
static XMP_Index FindIndexedItem ( XMP_Node * arrayNode, const XMP_VarString & indexStep, bool createNodes ) { XMP_Index index = 0; size_t chLim = indexStep.size() - 1; XMP_Assert ( (chLim >= 2) && (indexStep[0] == '[') && (indexStep[chLim] == ']') ); for ( size_t chNum = 1; chNum != chLim; ++chNum ) { XMP_Assert ( ('0' <= indexStep[chNum]) && (indexStep[chNum] <= '9') ); index = (index * 10) + (indexStep[chNum] - '0'); if ( index < 0 ) { XMP_Throw ( "Array index overflow", kXMPErr_BadXPath ); // ! Overflow, not truly negative. } } --index; // Change to a C-style, zero based index. if ( index < 0 ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath ); if ( (index == (XMP_Index)arrayNode->children.size()) && createNodes ) { // Append a new last+1 node. XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, kXMP_NewImplicitNode ); arrayNode->children.push_back ( newItem ); } // ! Don't throw here for a too large index. SetProperty will throw, GetProperty will not. if ( index >= (XMP_Index)arrayNode->children.size() ) index = -1; return index; } // FindIndexedItem
static void AppendNodeValue ( XMP_VarString & outputStr, const XMP_VarString & value, bool forAttribute ) { unsigned char * runStart = (unsigned char *) value.c_str(); unsigned char * runLimit = runStart + value.size(); unsigned char * runEnd; unsigned char ch; while ( runStart < runLimit ) { for ( runEnd = runStart; runEnd < runLimit; ++runEnd ) { ch = *runEnd; if ( forAttribute && (ch == '"') ) break; if ( (ch < 0x20) || (ch == '&') || (ch == '<') || (ch == '>') ) break; } outputStr.append ( (char *) runStart, (runEnd - runStart) ); if ( runEnd < runLimit ) { if ( ch < 0x20 ) { XMP_Assert ( (ch == kTab) || (ch == kLF) || (ch == kCR) ); char hexBuf[16]; memcpy ( hexBuf, "&#xn;", 6 ); // AUDIT: Length of "&#xn;" is 5, hexBuf size is 16. hexBuf[3] = kHexDigits[ch&0xF]; outputStr.append ( hexBuf, 5 ); } else { if ( ch == '"' ) { outputStr += """; } else if ( ch == '<' ) { outputStr += "<"; } else if ( ch == '>' ) { outputStr += ">"; } else { XMP_Assert ( ch == '&' ); outputStr += "&"; } } ++runEnd; } runStart = runEnd; } } // AppendNodeValue
static void SplitNameAndValue ( const XMP_VarString & selStep, XMP_VarString * nameStr, XMP_VarString * valueStr ) { XMP_StringPtr partBegin = selStep.c_str(); XMP_StringPtr partEnd; const XMP_StringPtr valueEnd = partBegin + (selStep.size() - 2); const char quote = *valueEnd; XMP_Assert ( (*partBegin == '[') && (*(valueEnd+1) == ']') ); XMP_Assert ( (selStep.size() >= 6) && ((quote == '"') || (quote == '\'')) ); // Extract the name part. ++partBegin; // Skip the opening '['. if ( *partBegin == '?' ) ++partBegin; for ( partEnd = partBegin+1; *partEnd != '='; ++partEnd ) {}; nameStr->assign ( partBegin, (partEnd - partBegin) ); // Extract the value part, reducing doubled quotes. XMP_Assert ( *(partEnd+1) == quote ); partBegin = partEnd + 2; valueStr->erase(); valueStr->reserve ( valueEnd - partBegin ); // Maximum length, don't optimize doubled quotes. for ( partEnd = partBegin; partEnd < valueEnd; ++partEnd ) { if ( (*partEnd == quote) && (*(partEnd+1) == quote) ) { ++partEnd; valueStr->append ( partBegin, (partEnd - partBegin) ); partBegin = partEnd+1; // ! Loop will increment partEnd again. } } valueStr->append ( partBegin, (partEnd - partBegin) ); // ! The loop does not add the last part. } // SplitNameAndValue
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; } }
void DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon ) { char buffer [20]; bool prevNormal; XMP_Status status = 0; XMP_StringPtr spanStart, spanEnd; XMP_StringPtr valueEnd = &value[0] + value.size(); spanStart = &value[0]; while ( spanStart < valueEnd ) { // Output the next span of regular characters. for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) { //if ( *spanEnd > 0x7F ) break; if ( (*spanEnd < 0x20) && (*spanEnd != kTab) && (*spanEnd != kLF) ) break; } if ( spanStart != spanEnd ) status = (*outProc) ( refCon, spanStart, (XMP_StringLen)(spanEnd-spanStart) ); if ( status != 0 ) break; spanStart = spanEnd; // Output the next span of irregular characters. prevNormal = true; for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) { if ( ((0x20 <= *spanEnd) /*&& (*spanEnd <= 0x7F)*/) || (*spanEnd == kTab) || (*spanEnd == kLF) ) break; char space = ' '; if ( prevNormal ) space = '<'; status = (*outProc) ( refCon, &space, 1 ); if ( status != 0 ) break; OutProcHexByte ( *spanEnd ); prevNormal = false; } if ( ! prevNormal ) { status = (*outProc) ( refCon, ">", 1 ); if ( status != 0 ) return; } spanStart = spanEnd; } } // DumpClearString
bool XMP_NamespaceTable::GetURI ( XMP_StringPtr _prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const { XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock ); bool found = false; XMP_Assert ( (_prefix != 0) && (*_prefix != 0) ); XMP_VarString prefix ( _prefix ); if ( prefix[prefix.size()-1] != ':' ) prefix += ':'; XMP_cStringMapPos prefixPos = this->prefixToURIMap.find ( prefix ); if ( prefixPos != this->prefixToURIMap.end() ) { if ( uriPtr != 0 ) *uriPtr = prefixPos->second.c_str(); if ( uriLen != 0 ) *uriLen = (XMP_StringLen)prefixPos->second.size(); found = true; } return found; } // XMP_NamespaceTable::GetURI
static RDFTermKind GetRDFTermKind ( const XMP_VarString & name ) { RDFTermKind term = kRDFTerm_Other; // Arranged to hopefully minimize the parse time for large XMP. if ( (name.size() > 4) && (strncmp ( name.c_str(), "rdf:", 4 ) == 0) ) { if ( name == "rdf:li" ) { term = kRDFTerm_li; } else if ( name == "rdf:parseType" ) { term = kRDFTerm_parseType; } else if ( name == "rdf:Description" ) { term = kRDFTerm_Description; } else if ( name == "rdf:about" ) { term = kRDFTerm_about; } else if ( name == "rdf:resource" ) { term = kRDFTerm_resource; } else if ( name == "rdf:RDF" ) { term = kRDFTerm_RDF; } else if ( name == "rdf:ID" ) { term = kRDFTerm_ID; } else if ( name == "rdf:nodeID" ) { term = kRDFTerm_nodeID; } else if ( name == "rdf:datatype" ) { term = kRDFTerm_datatype; } else if ( name == "rdf:aboutEach" ) { term = kRDFTerm_aboutEach; } else if ( name == "rdf:aboutEachPrefix" ) { term = kRDFTerm_aboutEachPrefix; } else if ( name == "rdf:bagID" ) { term = kRDFTerm_bagID; } } return term; } // GetRDFTermKind
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
static void SerializeAsRDF ( const XMPMeta & xmpObj, XMP_VarString & headStr, // Everything up to the padding. XMP_VarString & tailStr, // Everything after the padding. XMP_OptionBits options, XMP_StringPtr newline, XMP_StringPtr indentStr, XMP_Index baseIndent ) { const size_t treeNameLen = xmpObj.tree.name.size(); const size_t indentLen = strlen ( indentStr ); // First estimate the worst case space and reserve room in the output string. This optimization // avoids reallocating and copying the output as it grows. The initial count does not look at // the values of properties, so it does not account for character entities, e.g. 
 for newline. // Since there can be a lot of these in things like the base 64 encoding of a large thumbnail, // inflate the count by 1/4 (easy to do) to accommodate. // *** Need to include estimate for alias comments. size_t outputLen = 2 * (strlen(kPacketHeader) + strlen(kRDF_XMPMetaStart) + strlen(kRDF_RDFStart) + 3*baseIndent*indentLen); for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) { const XMP_Node * currSchema = xmpObj.tree.children[schemaNum]; outputLen += 2*(baseIndent+2)*indentLen + strlen(kRDF_SchemaStart) + treeNameLen + strlen(kRDF_SchemaEnd) + 2; outputLen += EstimateRDFSize ( currSchema, baseIndent+2, indentLen ); } outputLen += (outputLen >> 2); // Inflate by 1/4, an empirical fudge factor. // Now generate the RDF into the head string as UTF-8. XMP_Index level; headStr.erase(); headStr.reserve ( outputLen ); // Write the packet header PI. if ( ! (options & kXMP_OmitPacketWrapper) ) { for ( level = baseIndent; level > 0; --level ) headStr += indentStr; headStr += kPacketHeader; headStr += newline; } // Write the xmpmeta element's start tag. if ( ! (options & kXMP_OmitXMPMetaElement) ) { for ( level = baseIndent; level > 0; --level ) headStr += indentStr; headStr += kRDF_XMPMetaStart; headStr += kXMPCore_VersionMessage "\">"; headStr += newline; } // Write the rdf:RDF start tag. for ( level = baseIndent+1; level > 0; --level ) headStr += indentStr; headStr += kRDF_RDFStart; headStr += newline; // Write all of the properties. if ( options & kXMP_UseCompactFormat ) { SerializeCompactRDFSchemas ( xmpObj.tree, headStr, newline, indentStr, baseIndent ); } else { if ( xmpObj.tree.children.size() > 0 ) { for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) { const XMP_Node * currSchema = xmpObj.tree.children[schemaNum]; SerializePrettyRDFSchema ( xmpObj.tree.name, currSchema, headStr, options, newline, indentStr, baseIndent ); } } else { for ( XMP_Index level = baseIndent+2; level > 0; --level ) headStr += indentStr; headStr += kRDF_SchemaStart; // Special case an empty XMP object. headStr += '"'; headStr += xmpObj.tree.name; headStr += "\"/>"; headStr += newline; } } // Write the rdf:RDF end tag. for ( level = baseIndent+1; level > 0; --level ) headStr += indentStr; headStr += kRDF_RDFEnd; headStr += newline; // Write the xmpmeta end tag. if ( ! (options & kXMP_OmitXMPMetaElement) ) { for ( level = baseIndent; level > 0; --level ) headStr += indentStr; headStr += kRDF_XMPMetaEnd; headStr += newline; } // Write the packet trailer PI into the tail string as UTF-8. tailStr.erase(); if ( ! (options & kXMP_OmitPacketWrapper) ) { tailStr.reserve ( strlen(kPacketTrailer) + (strlen(indentStr) * baseIndent) ); for ( level = baseIndent; level > 0; --level ) tailStr += indentStr; tailStr += kPacketTrailer; if ( options & kXMP_ReadOnlyPacket ) tailStr[tailStr.size()-4] = 'r'; } // ! This assert is just a performance check, to see if the reserve was enough. // *** XMP_Assert ( headStr.size() <= outputLen ); // *** Don't use an assert. Think of some way to track this without risk of aborting the client. } // SerializeAsRDF
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