コード例 #1
0
ファイル: ParseRDF.cpp プロジェクト: BielBdeLuna/jp4tools
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
コード例 #2
0
ファイル: XMPCore_Impl.cpp プロジェクト: crass/dng4ps2
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
コード例 #3
0
ファイル: ExpatAdapter.cpp プロジェクト: ZacWalk/ImageWalker
void ExpatAdapter::ParseBuffer ( const void * buffer, size_t length, bool last /* = true */ )
{
	enum XML_Status status;
	
	if ( length == 0 ) {	// Expat does not like empty buffers.
		if ( ! last ) return;
		buffer = kOneSpace;
		length = 1;
	}
	
	status = XML_Parse ( this->parser, (const char *)buffer, length, last );
	
	#if BanAllEntityUsage
		if ( this->isAborted ) {
			XMP_Error error(kXMPErr_BadXML, "DOCTYPE is not allowed" )
			this->NotifyClient ( kXMPErrSev_Recoverable, error );
		}
	#endif

	if ( status != XML_STATUS_OK ) {
	
		XMP_StringPtr errMsg = "XML parsing failure";

		#if 0	// XMP_DebugBuild	// Disable for now to make test output uniform. Restore later with thread safety.
		
			// *** This is a good candidate for a callback error notification mechanism.
			// *** This code is not thread safe, the sExpatMessage isn't locked. But that's OK for debug usage.

			enum XML_Error expatErr = XML_GetErrorCode ( this->parser );
			const char *   expatMsg = XML_ErrorString ( expatErr );
			int errLine = XML_GetCurrentLineNumber ( this->parser );
		
			char msgBuffer[1000];
			// AUDIT: Use of sizeof(msgBuffer) for snprintf length is safe.
			snprintf ( msgBuffer, sizeof(msgBuffer), "# Expat error %d at line %d, \"%s\"", expatErr, errLine, expatMsg );
			sExpatMessage = msgBuffer;
			errMsg = sExpatMessage.c_str();

			#if  DumpXMLParseEvents
				if ( this->parseLog != 0 ) fprintf ( this->parseLog, "%s\n", errMsg, expatErr, errLine, expatMsg );
			#endif

		#endif

		XMP_Error error(kXMPErr_BadXML, errMsg);
		this->NotifyClient ( kXMPErrSev_Recoverable, error );

	}
	
}	// ExpatAdapter::ParseBuffer
コード例 #4
0
static void
CodePointToUTF8 ( UniCodePoint uniChar, XMP_VarString & utf8Str )
{
	size_t i, byteCount;
	XMP_Uns8 buffer [8];
	UniCodePoint cpTemp;
	
	if ( uniChar <= 0x7F ) {

		i = 7;
		byteCount = 1;
		buffer[7] = char(uniChar);
	
	} else {

		// ---------------------------------------------------------------------------------------
		// Copy the data bits from the low order end to the high order end, include the 0x80 mask.
		
		i = 8;
		cpTemp = uniChar;
		while ( cpTemp != 0 ) {
			-- i;	// Exit with i pointing to the last byte stored.
			buffer[i] = UnsByte(0x80) | (UnsByte(cpTemp) & 0x3F);
			cpTemp = cpTemp >> 6;
		}
		byteCount = 8 - i;	// The total number of bytes needed.
		XMP_Assert ( (2 <= byteCount) && (byteCount <= 6) );

		// -------------------------------------------------------------------------------------
		// Make sure the high order byte can hold the byte count mask, compute and set the mask.
		
		size_t bitCount = 0;	// The number of data bits in the first byte.
		for ( cpTemp = (buffer[i] & UnsByte(0x3F)); cpTemp != 0; cpTemp = cpTemp >> 1 ) bitCount += 1;
		if ( bitCount > (8 - (byteCount + 1)) ) byteCount += 1;
		
		i = 8 - byteCount;	// First byte index and mask shift count.
		XMP_Assert ( (0 <= i) && (i <= 6) );
		buffer[i] |= (UnsByte(0xFF) << i) & UnsByte(0xFF);	// AUDIT: Safe, i is between 0 and 6.
	
	}
	
	utf8Str.assign ( (char*)(&buffer[i]), byteCount );
	
}	// CodePointToUTF8
コード例 #5
0
ファイル: XMP_LibUtils.cpp プロジェクト: crass/dngconvert
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
コード例 #6
0
ファイル: XMP_LibUtils.cpp プロジェクト: crass/dngconvert
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
コード例 #7
0
void IOUtils::GetMatchingChildren ( XMP_StringVector & matchingChildList, const XMP_VarString & rootPath,
	const XMP_StringVector & regExStringVec, XMP_Bool includeFolders, XMP_Bool includeFiles, XMP_Bool prefixRootPath )
{
	try 
	{
		XMP_StringVector listOfAllResources;
		ListAllChildren (rootPath.c_str(), listOfAllResources, includeFolders, includeFiles, true);

		XMP_Bool matchRequired = !regExStringVec.empty();
		if ( matchRequired )
		{
			size_t childCount = listOfAllResources.size();
			for ( size_t index = 0; index < childCount; index++ )
			{
				XMP_Bool match = false;
				size_t regExpCount = regExStringVec.size();
				for ( size_t index2 = 0; index2 < regExpCount; index2++ )
				{
					XMP_RegExp regexObj ( regExStringVec[index2].c_str() );
					match = regexObj.Match ( listOfAllResources[index].c_str() );

					if ( match )
					{
						if ( prefixRootPath )
						{
							std::string fullPath = rootPath;
							if (fullPath[fullPath.length() - 1] != kDirChar )
								fullPath += kDirChar;
							fullPath += listOfAllResources[index];
							matchingChildList.push_back ( fullPath );
						}
						else
							matchingChildList.push_back ( listOfAllResources[index] );
						break;
					}
				}
			}
		}
	} catch ( XMP_Error & ) {
		// do nothing
	}
} // GetMatchingChildren
コード例 #8
0
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. &#xA; 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;
	
	std::string rdfstring;
	headStr.erase();
	rdfstring.reserve ( outputLen );

	// Write the rdf:RDF start tag.
	rdfstring += kRDF_RDFStart;
	rdfstring += newline;
	
	// Write all of the properties.
	if ( options & kXMP_UseCompactFormat ) {
		SerializeCompactRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent );
	} else {
		bool useCanonicalRDF = XMP_OptionIsSet ( options, kXMP_UseCanonicalFormat );
		SerializeCanonicalRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent, useCanonicalRDF );
	}

	// Write the rdf:RDF end tag.
	for ( level = baseIndent+1; level > 0; --level ) rdfstring += indentStr;
	rdfstring += kRDF_RDFEnd;
	// 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  "\"";
		std::string digestStr;
		unsigned char digestBin [16];
		if (options & kXMP_IncludeRDFHash)
		{
			std::string hashrdf;	
			MD5_CTX    context;
			MD5Init ( &context );
			MD5Update ( &context, (XMP_Uns8*)rdfstring.c_str(), (unsigned int)rdfstring.size() );
			MD5Final ( digestBin, &context );
			char buffer [40];
			for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) {
				XMP_Uns8 byte = digestBin[in];
				buffer[out]   = kHexDigits [ byte >> 4 ];
				buffer[out+1] = kHexDigits [ byte & 0xF ];
			}
			buffer[32] = 0;
			digestStr.append ( buffer );
			headStr += " rdfhash=\"";
			headStr += digestStr + "\"";
			headStr += " merged=\"0\"";
		}
		headStr += ">";
		headStr += newline;
	}
コード例 #9
0
static void
SerializeCompactRDFSchemas ( const XMP_Node & xmpTree,
							 XMP_VarString &  outputStr,
							 XMP_StringPtr	  newline,
							 XMP_StringPtr	  indentStr,
							 XMP_Index		  baseIndent )
{
	XMP_Index level;
	size_t schema, schemaLim;
	
	// Begin the rdf:Description start tag.
	for ( level = baseIndent+2; level > 0; --level ) outputStr += indentStr;
	outputStr += kRDF_SchemaStart;
	outputStr += '"';
	outputStr += xmpTree.name;
	outputStr += '"';
	
	// Write all necessary xmlns attributes.
	
	size_t totalLen = 8;	// Start at 8 for "xml:rdf:".
	XMP_cStringMapPos currPos = sNamespacePrefixToURIMap->begin();
	XMP_cStringMapPos endPos  = sNamespacePrefixToURIMap->end();
	for ( ; currPos != endPos; ++currPos ) totalLen += currPos->first.size();

	XMP_VarString usedNS;
	usedNS.reserve ( totalLen );
	usedNS = "xml:rdf:";

	for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) {
		const XMP_Node * currSchema = xmpTree.children[schema];
		DeclareUsedNamespaces ( currSchema, usedNS, outputStr, newline, indentStr, baseIndent+4 );
	}
	
	// Write the top level "attrProps" and close the rdf:Description start tag.
	bool allAreAttrs = true;
	for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) {
		const XMP_Node * currSchema = xmpTree.children[schema];
		allAreAttrs &= SerializeCompactRDFAttrProps ( currSchema, outputStr, newline, indentStr, baseIndent+3 );
	}
	if ( ! allAreAttrs ) {
		outputStr += ">";
		outputStr += newline;
	} else {
		outputStr += "/>";
		outputStr += newline;
		return;	// ! Done if all properties in all schema are written as attributes.
	}

	// Write the remaining properties for each schema.
	for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) {
		const XMP_Node * currSchema = xmpTree.children[schema];
		SerializeCompactRDFElemProps ( currSchema, outputStr, newline, indentStr, baseIndent+3 );
	}
	
	// Write the rdf:Description end tag.
	// *** Elide the end tag if everything (all props in all schema) is an attr.
	for ( level = baseIndent+2; level > 0; --level ) outputStr += indentStr;
	outputStr += kRDF_SchemaEnd;
	outputStr += newline;

}	// SerializeCompactRDFSchemas
コード例 #10
0
ファイル: XMPMeta-GetSet.cpp プロジェクト: hfiguiere/exempi
void
XMPMeta::SetLocalizedText ( XMP_StringPtr  schemaNS,
							XMP_StringPtr  arrayName,
							XMP_StringPtr  _genericLang,
							XMP_StringPtr  _specificLang,
							XMP_StringPtr  itemValue,
							XMP_OptionBits options )
{
	IgnoreParam(options);

	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) );	// Enforced by wrapper.

	XMP_VarString zGenericLang  ( _genericLang );
	XMP_VarString zSpecificLang ( _specificLang );
	NormalizeLangValue ( &zGenericLang );
	NormalizeLangValue ( &zSpecificLang );
	
	XMP_StringPtr genericLang  = zGenericLang.c_str();
	XMP_StringPtr specificLang = zSpecificLang.c_str();
	
	XMP_ExpandedXPath arrayPath;
	ExpandXPath ( schemaNS, arrayName, &arrayPath );
	
	// Find the array node and set the options if it was just created.
	XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_CreateNodes,
									  (kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate) );
	if ( arrayNode == 0 ) XMP_Throw ( "Failed to find or create array node", kXMPErr_BadXPath );
	if ( ! XMP_ArrayIsAltText(arrayNode->options) ) {
		if ( arrayNode->children.empty() && XMP_ArrayIsAlternate(arrayNode->options) ) {
			arrayNode->options |= kXMP_PropArrayIsAltText;
		} else {
			XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath );
		}
	}
	
	// Make sure the x-default item, if any, is first.
	
	size_t itemNum, itemLim;
	XMP_Node * xdItem = 0;
	bool haveXDefault = false;
	
	for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) {
		XMP_Node * currItem = arrayNode->children[itemNum];
		XMP_Assert ( XMP_PropHasLang(currItem->options) );
		if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) {
			XMP_Throw ( "Language qualifier must be first", kXMPErr_BadXPath );
		}
		if ( currItem->qualifiers[0]->value == "x-default" ) {
			xdItem = currItem;
			haveXDefault = true;
			break;
		}
	}
	
	if ( haveXDefault && (itemNum != 0) ) {
		XMP_Assert ( arrayNode->children[itemNum]->qualifiers[0]->value == "x-default" );
		XMP_Node * temp = arrayNode->children[0];
		arrayNode->children[0] = arrayNode->children[itemNum];
		arrayNode->children[itemNum] = temp;
	}
	
	// Find the appropriate item. ChooseLocalizedText will make sure the array is a language alternative.
		
	const XMP_Node * cItemNode;	// ! ChooseLocalizedText returns a pointer to a const node.
	XMP_CLTMatch match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &cItemNode );
	XMP_Node * itemNode = const_cast<XMP_Node*> ( cItemNode );

	const bool specificXDefault = XMP_LitMatch ( specificLang, "x-default" );
	
	switch ( match ) {

		case kXMP_CLT_NoValues :

			// Create the array items for the specificLang and x-default, with x-default first.
			AppendLangItem ( arrayNode, "x-default", itemValue );
			haveXDefault = true;
			if ( ! specificXDefault ) AppendLangItem ( arrayNode, specificLang, itemValue );
			break;
			
		case kXMP_CLT_SpecificMatch :
		
			if ( ! specificXDefault ) {
				// Update the specific item, update x-default if it matches the old value.
				if ( xdItem != NULL && haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) {
					SetNodeValue ( xdItem, itemValue );
				}
				SetNodeValue ( itemNode, itemValue );	// ! Do this after the x-default check!
			} else {
				// Update all items whose values match the old x-default value.
				XMP_Assert ( xdItem != NULL && haveXDefault && (xdItem == itemNode) );
				for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) {
					XMP_Node * currItem = arrayNode->children[itemNum];
					if ( (currItem == xdItem) || (currItem->value != xdItem->value) ) continue;
					SetNodeValue ( currItem, itemValue );
				}
				SetNodeValue ( xdItem, itemValue );	// And finally do the x-default item.
			}
			break;

		case kXMP_CLT_SingleGeneric :
		
			// Update the generic item, update x-default if it matches the old value.
			if (  xdItem != NULL && haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) {
				SetNodeValue ( xdItem, itemValue );
			}
			SetNodeValue ( itemNode, itemValue );	// ! Do this after the x-default check!
			break;

		case kXMP_CLT_MultipleGeneric :
		
			// Create the specific language, ignore x-default.
			AppendLangItem ( arrayNode, specificLang, itemValue );
			if ( specificXDefault ) haveXDefault = true;
			break;
			
		case kXMP_CLT_XDefault :

			// Create the specific language, update x-default if it was the only item.
			if ( arrayNode->children.size() == 1 ) SetNodeValue ( xdItem, itemValue );
			AppendLangItem ( arrayNode, specificLang, itemValue );
			break;

		case kXMP_CLT_FirstItem	:

			// Create the specific language, don't add an x-default item.
			AppendLangItem ( arrayNode, specificLang, itemValue );
			if ( specificXDefault ) haveXDefault = true;
			break;
			
		default :
			XMP_Throw ( "Unexpected result from ChooseLocalizedText", kXMPErr_InternalFailure );

	}

	// Add an x-default at the front if needed.
	if ( (! haveXDefault) && (arrayNode->children.size() == 1) ) {
		AppendLangItem ( arrayNode, "x-default", itemValue );
	}

}	// SetLocalizedText
コード例 #11
0
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. &#xA; 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
コード例 #12
0
static void
SerializePrettyRDFSchema ( const XMP_VarString & treeName,
						   const XMP_Node *		 schemaNode,
						   XMP_VarString &		 outputStr,
						   XMP_OptionBits		 options,
						   XMP_StringPtr		 newline,
						   XMP_StringPtr		 indentStr,
						   XMP_Index			 baseIndent )
{
	XMP_Assert ( schemaNode->options & kXMP_SchemaNode );
	XMP_Assert ( schemaNode->qualifiers.empty() );
	
	// Write the rdf:Description start tag with the namespace declarations.
	
	XMP_Index level;
	for ( level = baseIndent+2; level > 0; --level ) outputStr += indentStr;
	outputStr += kRDF_SchemaStart;
	outputStr += '"';
	outputStr += treeName;
	outputStr += '"';

	size_t totalLen = 8;	// Start at 8 for "xml:rdf:".
	XMP_cStringMapPos currPos = sNamespacePrefixToURIMap->begin();
	XMP_cStringMapPos endPos  = sNamespacePrefixToURIMap->end();
	for ( ; currPos != endPos; ++currPos ) totalLen += currPos->first.size();

	XMP_VarString usedNS;
	usedNS.reserve ( totalLen );
	usedNS = "xml:rdf:";
	DeclareUsedNamespaces ( schemaNode, usedNS, outputStr, newline, indentStr, baseIndent+4 );

	outputStr += ">";
	outputStr += newline;
	
	// Write alias comments, if wanted.

	if ( options & kXMP_WriteAliasComments ) {	// *** Hoist into a routine, used for Plain XMP also.

		#if 0	// *** Buggy, disable for now.
		
		XMP_cAliasMapPos aliasPos = sRegisteredAliasMap->begin();
		XMP_cAliasMapPos aliasEnd = sRegisteredAliasMap->end();
		
		for ( ; aliasPos != aliasEnd; ++aliasPos ) {

			size_t nsPos = aliasPos->first.find ( schemaNode->value );
			if ( nsPos == XMP_VarString::npos ) continue;
			XMP_Assert ( nsPos == 0 );

			for ( level = baseIndent+3; level > 0; --level ) outputStr += indentStr;

			outputStr += "<!-- ";
			outputStr += aliasPos->first;
			outputStr += " is aliased to ";
			for ( size_t step = 1, stepLim = aliasPos->second.size(); step != stepLim; ++step ) {
				outputStr += aliasPos->second[step].step;
			}
			outputStr += " -->";
			outputStr += newline;

		}
		
		#endif

	}
	
	// Write each of the schema's actual properties.
	for ( size_t propNum = 0, propLim = schemaNode->children.size(); propNum < propLim; ++propNum ) {
		const XMP_Node * currProp = schemaNode->children[propNum];
		SerializePrettyRDFProperty ( currProp, outputStr, newline, indentStr, baseIndent+3 );
	}
	
	// Write the rdf:Description end tag.
	for ( level = baseIndent+2; level > 0; --level ) outputStr += indentStr;
	outputStr += kRDF_SchemaEnd;
	outputStr += newline;

}	// SerializePrettyRDFSchema
コード例 #13
0
ファイル: XMPMeta-GetSet.cpp プロジェクト: hfiguiere/exempi
void
XMPMeta::DeleteLocalizedText ( XMP_StringPtr schemaNS,
                               XMP_StringPtr arrayName,
                               XMP_StringPtr _genericLang,
                               XMP_StringPtr _specificLang )
{
	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) );	// Enforced by wrapper.

	XMP_VarString zGenericLang  ( _genericLang );
	XMP_VarString zSpecificLang ( _specificLang );
	NormalizeLangValue ( &zGenericLang );
	NormalizeLangValue ( &zSpecificLang );

	XMP_StringPtr genericLang  = zGenericLang.c_str();
	XMP_StringPtr specificLang = zSpecificLang.c_str();

	XMP_ExpandedXPath arrayPath;
	ExpandXPath ( schemaNS, arrayName, &arrayPath );
	
	// Find the LangAlt array and the selected array item.

	XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly );
	if ( arrayNode == 0 ) return;
	size_t arraySize = arrayNode->children.size();

	XMP_CLTMatch match;
	XMP_Node * itemNode;

	match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, (const XMP_Node **) &itemNode );
	if ( match != kXMP_CLT_SpecificMatch ) return;

	size_t itemIndex = 0;
	for ( ; itemIndex < arraySize; ++itemIndex ) {
		if ( arrayNode->children[itemIndex] == itemNode ) break;
	}
	XMP_Enforce ( itemIndex < arraySize );
	
	// Decide if the selected item is x-default or not, find relevant matching item.
	
	bool itemIsXDefault = false;
	if ( ! itemNode->qualifiers.empty() ) {
		XMP_Node * qualNode = itemNode->qualifiers[0];
		if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) itemIsXDefault = true;
	}
	
	if ( itemIsXDefault && (itemIndex != 0) ) {	// Enforce the x-default is first policy.
		XMP_Node * temp = arrayNode->children[0];
		arrayNode->children[0] = arrayNode->children[itemIndex];
		arrayNode->children[itemIndex] = temp;
		itemIndex = 0;
	}
	
	XMP_Node * assocNode = 0;
	size_t assocIndex;
	
	if ( itemIsXDefault ) {

		for ( assocIndex = 1; assocIndex < arraySize; ++assocIndex ) {
			if ( arrayNode->children[assocIndex]->value == itemNode->value ) {
				assocNode = arrayNode->children[assocIndex];
				break;
			}
		}

	} else if ( itemIndex > 0 ) {

		XMP_Node * itemZero = arrayNode->children[0];
		if ( itemZero->value == itemNode->value ) {
			XMP_Node * qualNode = itemZero->qualifiers[0];
			if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) {
				assocNode = arrayNode->children[0];
				assocIndex = 0;
			}
		}

	}
	
	// Delete the appropriate nodes.
	
	XMP_NodePtrPos arrayBegin = arrayNode->children.begin();
	
	if ( assocNode == 0 ) {
		arrayNode->children.erase ( arrayBegin + itemIndex );
	} else if ( itemIndex < assocIndex ) {
		arrayNode->children.erase ( arrayBegin + assocIndex );
		arrayNode->children.erase ( arrayBegin + itemIndex );
	} else {
		arrayNode->children.erase ( arrayBegin + itemIndex );
		arrayNode->children.erase ( arrayBegin + assocIndex );
	}

	delete itemNode;
	if ( assocNode != 0 ) delete assocNode;

}	// DeleteLocalizedText
コード例 #14
0
ファイル: XMPIterator.cpp プロジェクト: dtbinh/dviz
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
コード例 #15
0
ファイル: ExpatAdapter.cpp プロジェクト: KDE/digikam
namespace DngXmpSdk {
#if XMP_WinBuild
    #pragma warning ( disable : 4996 )	// '...' was declared deprecated
#endif

// *** Set memory handlers.

#ifndef DumpXMLParseEvents
    #define DumpXMLParseEvents	0
#endif

#define FullNameSeparator	'@'

// =================================================================================================

static void StartNamespaceDeclHandler    ( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri );
static void EndNamespaceDeclHandler      ( void * userData, XMP_StringPtr prefix );

static void StartElementHandler          ( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs );
static void EndElementHandler            ( void * userData, XMP_StringPtr name );

static void CharacterDataHandler         ( void * userData, XMP_StringPtr cData, int len );
static void StartCdataSectionHandler     ( void * userData );
static void EndCdataSectionHandler       ( void * userData );

static void ProcessingInstructionHandler ( void * userData, XMP_StringPtr target, XMP_StringPtr data );
static void CommentHandler               ( void * userData, XMP_StringPtr comment );

#if BanAllEntityUsage

    // For now we do this by banning DOCTYPE entirely. This is easy and consistent with what is
    // available in recent Java XML parsers. Another, somewhat less drastic, approach would be to
    // ban all entity declarations. We can't allow declarations and ban references, Expat does not
    // call the SkippedEntityHandler for references in attribute values.

    // ! Standard entities (&amp;, &lt;, &gt;, &quot;, &apos;, and numeric character references) are
    // ! not banned. Expat handles them transparently no matter what.

    static void StartDoctypeDeclHandler ( void * userData, XMP_StringPtr doctypeName,
                                          XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset );

#endif

// =================================================================================================

extern "C" ExpatAdapter * XMP_NewExpatAdapter()
{
    return new ExpatAdapter;
}	// XMP_NewExpatAdapter

// =================================================================================================

ExpatAdapter::ExpatAdapter() : parser(0)
{

    #if XMP_DebugBuild
        this->elemNesting = 0;
        #if DumpXMLParseEvents
            if ( this->parseLog == 0 ) this->parseLog = stdout;
        #endif
    #endif

    this->parser = XML_ParserCreateNS ( 0, FullNameSeparator );
    if ( this->parser == 0 ) XMP_Throw ( "Failure creating Expat parser", kXMPErr_ExternalFailure );

    XML_SetUserData ( this->parser, this );

    XML_SetNamespaceDeclHandler ( this->parser, StartNamespaceDeclHandler, EndNamespaceDeclHandler );
    XML_SetElementHandler ( this->parser, StartElementHandler, EndElementHandler );

    XML_SetCharacterDataHandler ( this->parser, CharacterDataHandler );
    XML_SetCdataSectionHandler ( this->parser, StartCdataSectionHandler, EndCdataSectionHandler );

    XML_SetProcessingInstructionHandler ( this->parser, ProcessingInstructionHandler );
    XML_SetCommentHandler ( this->parser, CommentHandler );

    #if BanAllEntityUsage
        XML_SetStartDoctypeDeclHandler ( this->parser, StartDoctypeDeclHandler );
        isAborted = false;
    #endif

    this->parseStack.push_back ( &this->tree );	// Push the XML root node.

}	// ExpatAdapter::ExpatAdapter

// =================================================================================================

ExpatAdapter::~ExpatAdapter()
{

    if ( this->parser != 0 ) XML_ParserFree ( this->parser );
    this->parser = 0;

}	// ExpatAdapter::~ExpatAdapter

// =================================================================================================

#if XMP_DebugBuild
    static XMP_VarString sExpatMessage;
#endif

static const char * kOneSpace = " ";

void ExpatAdapter::ParseBuffer ( const void * buffer, size_t length, bool last /* = true */ )
{
    enum XML_Status status;

    if ( length == 0 ) {	// Expat does not like empty buffers.
        if ( ! last ) return;
        buffer = kOneSpace;
        length = 1;
    }

    status = XML_Parse ( this->parser, (const char *)buffer, length, last );

    #if BanAllEntityUsage
        if ( this->isAborted ) XMP_Throw ( "DOCTYPE is not allowed", kXMPErr_BadXML );
    #endif

    if ( status != XML_STATUS_OK ) {

        XMP_StringPtr errMsg = "XML parsing failure";

        #if 0	// XMP_DebugBuild	// Disable for now to make test output uniform. Restore later with thread safety.

            // *** This is a good candidate for a callback error notification mechanism.
            // *** This code is not thread safe, the sExpatMessage isn't locked. But that's OK for debug usage.

            enum XML_Error expatErr = XML_GetErrorCode ( this->parser );
            const char *   expatMsg = XML_ErrorString ( expatErr );
            int errLine = XML_GetCurrentLineNumber ( this->parser );

            char msgBuffer[1000];
            // AUDIT: Use of sizeof(msgBuffer) for snprintf length is safe.
            snprintf ( msgBuffer, sizeof(msgBuffer), "# Expat error %d at line %d, \"%s\"", expatErr, errLine, expatMsg );
            sExpatMessage = msgBuffer;
            errMsg = sExpatMessage.c_str();

            #if  DumpXMLParseEvents
                if ( this->parseLog != 0 ) fprintf ( this->parseLog, "%s\n", errMsg, expatErr, errLine, expatMsg );
            #endif

        #endif

        XMP_Throw ( errMsg, kXMPErr_BadXML );

    }

}	// ExpatAdapter::ParseBuffer

// =================================================================================================
// =================================================================================================

#if XMP_DebugBuild & DumpXMLParseEvents

    static inline void PrintIndent ( FILE * file, size_t count )
    {
        for ( ; count > 0; --count ) fprintf ( file, "  " );
    }

#endif

// =================================================================================================

static void SetQualName ( XMP_StringPtr fullName, XML_Node * node )
{
    // Expat delivers the full name as a catenation of namespace URI, separator, and local name.

    // As a compatibility hack, an "about" or "ID" attribute of an rdf:Description element is
    // changed to "rdf:about" or rdf:ID. Easier done here than in the RDF recognizer.

    // As a bug fix hack, change a URI of "http://purl.org/dc/1.1/" to ""http://purl.org/dc/elements/1.1/.
    // Early versions of Flash that put XMP in SWF used a bad URI for the dc: namespace.

    // ! This code presumes the RDF namespace prefix is "rdf".

    size_t sepPos = strlen(fullName);
    for ( --sepPos; sepPos > 0; --sepPos ) {
        if ( fullName[sepPos] == FullNameSeparator ) break;
    }

    if ( fullName[sepPos] == FullNameSeparator ) {

        XMP_StringPtr prefix;
        XMP_StringLen prefixLen;
        XMP_StringPtr localPart = fullName + sepPos + 1;

        node->ns.assign ( fullName, sepPos );
        if ( node->ns == "http://purl.org/dc/1.1/" ) node->ns = "http://purl.org/dc/elements/1.1/";

        bool found = XMPMeta::GetNamespacePrefix ( node->ns.c_str(), &prefix, &prefixLen );
        if ( ! found ) XMP_Throw ( "Unknown URI in Expat full name", kXMPErr_ExternalFailure );
        node->nsPrefixLen = prefixLen;	// ! Includes the ':'.

        node->name = prefix;
        node->name += localPart;

    } else {

        node->name = fullName;	// The name is not in a namespace.

        if ( node->parent->name == "rdf:Description" ) {
            if ( node->name == "about" ) {
                node->ns   = kXMP_NS_RDF;
                node->name = "rdf:about";
                node->nsPrefixLen = 4;	// ! Include the ':'.
            } else if ( node->name == "ID" ) {
                node->ns   = kXMP_NS_RDF;
                node->name = "rdf:ID";
                node->nsPrefixLen = 4;	// ! Include the ':'.
            }
        }

    }

}	// SetQualName

// =================================================================================================

static void StartNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri )
{
    IgnoreParam(userData);

    // As a bug fix hack, change a URI of "http://purl.org/dc/1.1/" to ""http://purl.org/dc/elements/1.1/.
    // Early versions of Flash that put XMP in SWF used a bad URI for the dc: namespace.

    #if XMP_DebugBuild & DumpXMLParseEvents		// Avoid unused variable warning.
        ExpatAdapter * thiz = (ExpatAdapter*)userData;
    #endif

    if ( prefix == 0 ) prefix = "_dflt_";	// Have default namespace.
    if ( uri == 0 ) return;	// Ignore, have xmlns:pre="", no URI to register.

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "StartNamespace: %s - \"%s\"\n", prefix, uri );
        }
    #endif

    if ( XMP_LitMatch ( uri, "http://purl.org/dc/1.1/" ) ) uri = "http://purl.org/dc/elements/1.1/";
    (void) XMPMeta::RegisterNamespace ( uri, prefix, &voidStringPtr, &voidStringLen );

}	// StartNamespaceDeclHandler

// =================================================================================================

static void EndNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix )
{
    IgnoreParam(userData);

    #if XMP_DebugBuild & DumpXMLParseEvents		// Avoid unused variable warning.
        ExpatAdapter * thiz = (ExpatAdapter*)userData;
    #endif

    if ( prefix == 0 ) prefix = "_dflt_";	// Have default namespace.

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "EndNamespace: %s\n", prefix );
        }
    #endif

    // ! Nothing to do, Expat has done all of the XML processing.

}	// EndNamespaceDeclHandler

// =================================================================================================

static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs )
{
    XMP_Assert ( attrs != 0 );
    ExpatAdapter * thiz = (ExpatAdapter*)userData;

    size_t attrCount = 0;
    for ( XMP_StringPtr* a = attrs; *a != 0; ++a ) ++attrCount;
    if ( (attrCount & 1) != 0 )	XMP_Throw ( "Expat attribute info has odd length", kXMPErr_ExternalFailure );
    attrCount = attrCount/2;	// They are name/value pairs.

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "StartElement: %s, %d attrs", name, attrCount );
            for ( XMP_StringPtr* attr = attrs; *attr != 0; attr += 2 ) {
                XMP_StringPtr attrName = *attr;
                XMP_StringPtr attrValue = *(attr+1);
                fprintf ( thiz->parseLog, ", %s = \"%s\"", attrName, attrValue );
            }
            fprintf ( thiz->parseLog, "\n" );
        }
    #endif

    XML_Node * parentNode = thiz->parseStack.back();
    XML_Node * elemNode   = new XML_Node ( parentNode, "", kElemNode );

    SetQualName ( name, elemNode );

    for ( XMP_StringPtr* attr = attrs; *attr != 0; attr += 2 ) {

        XMP_StringPtr attrName = *attr;
        XMP_StringPtr attrValue = *(attr+1);
        XML_Node * attrNode = new XML_Node ( elemNode, "", kAttrNode );

        SetQualName ( attrName, attrNode );
        attrNode->value = attrValue;
        if ( attrNode->name == "xml:lang" ) NormalizeLangValue ( &attrNode->value );
        elemNode->attrs.push_back ( attrNode );

    }

    parentNode->content.push_back ( elemNode );
    thiz->parseStack.push_back ( elemNode );

    if ( elemNode->name == "rdf:RDF" ) {
        thiz->rootNode = elemNode;
        ++thiz->rootCount;
    }
    #if XMP_DebugBuild
        ++thiz->elemNesting;
    #endif

}	// StartElementHandler

// =================================================================================================

static void EndElementHandler ( void * userData, XMP_StringPtr name )
{
    IgnoreParam(name);

    ExpatAdapter * thiz = (ExpatAdapter*)userData;

    #if XMP_DebugBuild
        --thiz->elemNesting;
    #endif
    (void) thiz->parseStack.pop_back();

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "EndElement: %s\n", name );
        }
    #endif

}	// EndElementHandler

// =================================================================================================

static void CharacterDataHandler ( void * userData, XMP_StringPtr cData, int len )
{
    ExpatAdapter * thiz = (ExpatAdapter*)userData;

    if ( (cData == 0) || (len == 0) ) { cData = ""; len = 0; }

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "CharContent: \"" );
            for ( int i = 0; i < len; ++i ) fprintf ( thiz->parseLog, "%c", cData[i] );
            fprintf ( thiz->parseLog, "\"\n" );
        }
    #endif

    XML_Node * parentNode = thiz->parseStack.back();
    XML_Node * cDataNode  = new XML_Node ( parentNode, "", kCDataNode );

    cDataNode->value.assign ( cData, len );
    parentNode->content.push_back ( cDataNode );

}	// CharacterDataHandler

// =================================================================================================

static void StartCdataSectionHandler ( void * userData )
{
    IgnoreParam(userData);

    #if XMP_DebugBuild & DumpXMLParseEvents		// Avoid unused variable warning.
        ExpatAdapter * thiz = (ExpatAdapter*)userData;
    #endif

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "StartCDATA\n" );
        }
    #endif

    // *** Since markup isn't recognized inside CDATA, this affects XMP's double escaping.

}	// StartCdataSectionHandler

// =================================================================================================

static void EndCdataSectionHandler ( void * userData )
{
    IgnoreParam(userData);

    #if XMP_DebugBuild & DumpXMLParseEvents		// Avoid unused variable warning.
        ExpatAdapter * thiz = (ExpatAdapter*)userData;
    #endif

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "EndCDATA\n" );
        }
    #endif

}	// EndCdataSectionHandler

// =================================================================================================

static void ProcessingInstructionHandler ( void * userData, XMP_StringPtr target, XMP_StringPtr data )
{
    XMP_Assert ( target != 0 );
    ExpatAdapter * thiz = (ExpatAdapter*)userData;

    if ( ! XMP_LitMatch ( target, "xpacket" ) ) return;	// Ignore all PIs except the XMP packet wrapper.
    if ( data == 0 ) data = "";

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "PI: %s - \"%s\"\n", target, data );
        }
    #endif

    XML_Node * parentNode = thiz->parseStack.back();
    XML_Node * piNode  = new XML_Node ( parentNode, target, kPINode );

    piNode->value.assign ( data );
    parentNode->content.push_back ( piNode );

}	// ProcessingInstructionHandler

// =================================================================================================

static void CommentHandler ( void * userData, XMP_StringPtr comment )
{
    IgnoreParam(userData);

    #if XMP_DebugBuild & DumpXMLParseEvents		// Avoid unused variable warning.
        ExpatAdapter * thiz = (ExpatAdapter*)userData;
    #endif

    if ( comment == 0 ) comment = "";

    #if XMP_DebugBuild & DumpXMLParseEvents
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "Comment: \"%s\"\n", comment );
        }
    #endif

    // ! Comments are ignored.

}	// CommentHandler

// =================================================================================================

#if BanAllEntityUsage
static void StartDoctypeDeclHandler ( void * userData, XMP_StringPtr doctypeName,
                                      XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset )
{
    IgnoreParam(userData);

    ExpatAdapter * thiz = (ExpatAdapter*)userData;

    #if XMP_DebugBuild & DumpXMLParseEvents		// Avoid unused variable warning.
        if ( thiz->parseLog != 0 ) {
            PrintIndent ( thiz->parseLog, thiz->elemNesting );
            fprintf ( thiz->parseLog, "DocType: \"%s\"\n", doctypeName );
        }
    #endif

    thiz->isAborted = true;	// ! Can't throw an exception across the plain C Expat frames.
    (void) XML_StopParser ( thiz->parser, XML_FALSE /* not resumable */ );

}	// StartDoctypeDeclHandler
#endif

} // namespace DngXmpSdk
コード例 #16
0
ファイル: XMPCore_Impl.cpp プロジェクト: crass/dng4ps2
void
ExpandXPath	( XMP_StringPtr			schemaNS,
			  XMP_StringPtr			propPath,
			  XMP_ExpandedXPath *	expandedXPath )
{
	XMP_Assert ( (schemaNS != 0) && (propPath != 0) && (*propPath != 0) && (expandedXPath != 0) );
	
	XMP_StringPtr	stepBegin, stepEnd;
	XMP_StringPtr	qualName, nameEnd;
	XMP_VarString	currStep;
		
	size_t resCount = 2;	// Guess at the number of steps. At least 2, plus 1 for each '/' or '['.
	for ( stepEnd = propPath; *stepEnd != 0; ++stepEnd ) {
		if ( (*stepEnd == '/') || (*stepEnd == '[') ) ++resCount;
	}
	
	expandedXPath->clear();
	expandedXPath->reserve ( resCount );
	
	// -------------------------------------------------------------------------------------------
	// Pull out the first component and do some special processing on it: add the schema namespace
	// prefix and see if it is an alias. The start must be a qualName.
	
	stepBegin = propPath;
	stepEnd = stepBegin;
	while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
	if ( stepEnd == stepBegin ) XMP_Throw ( "Empty initial XPath step", kXMPErr_BadXPath );
	currStep.assign ( stepBegin, (stepEnd - stepBegin) );
	
	VerifyXPathRoot ( schemaNS, currStep.c_str(), expandedXPath );

	XMP_OptionBits stepFlags = kXMP_StructFieldStep;	
	if ( sRegisteredAliasMap->find ( (*expandedXPath)[kRootPropStep].step ) != sRegisteredAliasMap->end() ) {
		stepFlags |= kXMP_StepIsAlias;
	}
	(*expandedXPath)[kRootPropStep].options |= stepFlags;
		
	// -----------------------------------------------------
	// Now continue to process the rest of the XPath string.

	while ( *stepEnd != 0 ) {

		stepBegin = stepEnd;
		if ( *stepBegin == '/' ) ++stepBegin;
		if ( *stepBegin == '*' ) {
			++stepBegin;
			if ( *stepBegin != '[' ) XMP_Throw ( "Missing '[' after '*'", kXMPErr_BadXPath );
		}
		stepEnd = stepBegin;

		if ( *stepBegin != '[' ) {
		
			// A struct field or qualifier.
			qualName = stepBegin;
			while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd;
			nameEnd = stepEnd;
			stepFlags = kXMP_StructFieldStep;	// ! Touch up later, also changing '@' to '?'.
			
		} else {
		
			// One of the array forms.
		
			++stepEnd;	// Look at the character after the leading '['.
			
			if ( ('0' <= *stepEnd) && (*stepEnd <= '9') ) {

				// A numeric (decimal integer) array index.
				while ( (*stepEnd != 0) && ('0' <= *stepEnd) && (*stepEnd <= '9') ) ++stepEnd;
				if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for integer array index", kXMPErr_BadXPath );
				stepFlags = kXMP_ArrayIndexStep;

			} else {

				// Could be "[last()]" or one of the selector forms. Find the ']' or '='.
				
				while ( (*stepEnd != 0) && (*stepEnd != ']') && (*stepEnd != '=') ) ++stepEnd;
				if ( *stepEnd == 0 ) XMP_Throw ( "Missing ']' or '=' for array index", kXMPErr_BadXPath );

				if ( *stepEnd == ']' ) {

					if ( strncmp ( "[last()", stepBegin, (stepEnd - stepBegin) ) != 0 ) {
						XMP_Throw ( "Invalid non-numeric array index", kXMPErr_BadXPath );
					}
					stepFlags = kXMP_ArrayLastStep;

				} else {

					qualName = stepBegin+1;
					nameEnd = stepEnd;
					++stepEnd;	// Absorb the '=', remember the quote.
					const char quote = *stepEnd;
					if ( (quote != '\'') && (quote != '"') ) {
						XMP_Throw ( "Invalid quote in array selector", kXMPErr_BadXPath );
					}

					++stepEnd;	// Absorb the leading quote.
					while ( *stepEnd != 0 ) {
						if ( *stepEnd == quote ) {
							if ( *(stepEnd+1) != quote ) break;
							++stepEnd;
						}
						++stepEnd;
					}
					if ( *stepEnd == 0 ) {
						XMP_Throw ( "No terminating quote for array selector", kXMPErr_BadXPath );
					}
					++stepEnd;	// Absorb the trailing quote.
					
					stepFlags = kXMP_FieldSelectorStep;	// ! Touch up later, also changing '@' to '?'.

				}

			}

			if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for array index", kXMPErr_BadXPath );
			++stepEnd;
			
		}

		if ( stepEnd == stepBegin ) XMP_Throw ( "Empty XPath step", kXMPErr_BadXPath );
		currStep.assign ( stepBegin, (stepEnd - stepBegin) );

		if ( GetStepKind ( stepFlags ) == kXMP_StructFieldStep ) {

			if ( currStep[0] == '@' ) {
				currStep[0] = '?';
				if ( currStep != "?xml:lang" ) XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
			}
			if ( currStep[0] == '?' ) {
				++qualName;
				stepFlags = kXMP_QualifierStep;
			}
			VerifyQualName ( qualName, nameEnd );

		} else if ( GetStepKind ( stepFlags ) == kXMP_FieldSelectorStep ) {

			if ( currStep[1] == '@' ) {
				currStep[1] = '?';
				if ( strncmp ( currStep.c_str(), "[?xml:lang=", 11 ) != 0 ) {
					XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath );
				}
			}
			if ( currStep[1] == '?' ) {
				++qualName;
				stepFlags = kXMP_QualSelectorStep;
			}
			VerifyQualName ( qualName, nameEnd );

		}

		expandedXPath->push_back ( XPathStepInfo ( currStep, stepFlags ) );

	}

}	// ExpandXPath
コード例 #17
0
ファイル: XMPIterator.cpp プロジェクト: JanX2/exempi
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
コード例 #18
0
ファイル: XMPIterator.cpp プロジェクト: dtbinh/dviz
static void
AddNodeOffspring ( IterInfo & info, IterNode & iterParent, const XMP_Node * xmpParent )
{
	XMP_VarString currPath ( iterParent.fullPath );
	size_t        leafOffset = iterParent.fullPath.size();
	
	if ( (! xmpParent->qualifiers.empty()) && (! (info.options & kXMP_IterOmitQualifiers)) ) {

		#if TraceIterators
			printf ( "    Adding qualifiers of %s\n", currPath.c_str() );
		#endif

		currPath += "/?";	// All qualifiers are named and use paths like "Prop/?Qual".
		leafOffset += 2;
		
		for ( size_t qualNum = 0, qualLim = xmpParent->qualifiers.size(); qualNum != qualLim; ++qualNum ) {
			const XMP_Node * xmpQual = xmpParent->qualifiers[qualNum];
			currPath += xmpQual->name;
			iterParent.qualifiers.push_back ( IterNode ( xmpQual->options, currPath, leafOffset ) );
			currPath.erase ( leafOffset );
			#if TraceIterators
				printf ( "        %s\n", xmpQual->name.c_str() );
			#endif
		}
		
		leafOffset -= 2;
		currPath.erase ( leafOffset );

	}

	if ( ! xmpParent->children.empty() ) {
	
		#if TraceIterators
			printf ( "    Adding children of %s\n", currPath.c_str() );
		#endif

		XMP_Assert ( xmpParent->options & kXMP_PropCompositeMask );
		
		if ( xmpParent->options & kXMP_PropValueIsStruct ) {
			currPath += '/';
			leafOffset += 1;
		}
		
		for ( size_t childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) {
			const XMP_Node * xmpChild = xmpParent->children[childNum];
			if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) {
				currPath += xmpChild->name;
			} else {
				char buffer [32];	// AUDIT: Using sizeof(buffer) below for snprintf length is safe.
				snprintf ( buffer, sizeof(buffer), "[%lu]", static_cast<unsigned long>(childNum+1) );	// ! XPath indices are one-based.
				currPath += buffer;
			}
			iterParent.children.push_back ( IterNode ( xmpChild->options, currPath, leafOffset ) );
			currPath.erase ( leafOffset );
			#if TraceIterators
				printf ( "        %s\n", (iterParent.children.back().fullPath.c_str() + leafOffset) );
			#endif
		}
	
	}

}	// AddNodeOffspring
コード例 #19
0
static void
ApplyQuotes ( XMP_VarString * item, UniCodePoint openQuote, UniCodePoint closeQuote, bool allowCommas )
{
	bool	prevSpace	= false;
	size_t	charOffset, charLen;
	UniCharKind		charKind;
	UniCodePoint	uniChar;
	
	// -----------------------------------------------------------------------------------------
	// See if there are any separators in the value. Stop at the first occurrance. This is a bit
	// tricky in order to make typical typing work conveniently. The purpose of applying quotes
	// is to preserve the values when splitting them back apart. That is CatenateContainerItems
	// and SeparateContainerItems must round trip properly. For the most part we only look for
	// separators here. Internal quotes, as in -- Irving "Bud" Jones -- won't cause problems in
	// the separation. An initial quote will though, it will make the value look quoted.

	charOffset = 0;
	ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar );
	
	if ( charKind != UCK_quote ) {
	
	for ( charOffset = 0; size_t(charOffset) < item->size(); charOffset += charLen ) {

			ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar );

			if ( charKind == UCK_space ) {
				if ( prevSpace ) break; // Multiple spaces are a separator.
				prevSpace = true;
			} else {
				prevSpace = false;
				if ( (charKind == UCK_semicolon) || (charKind == UCK_control) ) break;
				if ( (charKind == UCK_comma) && (! allowCommas) ) break;
			}

		}
	
	}
	
	if ( size_t(charOffset) < item->size() ) {
	
		// --------------------------------------------------------------------------------------
		// Create a quoted copy, doubling any internal quotes that match the outer ones. Internal
		// quotes did not stop the "needs quoting" search, but they do need doubling. So we have
		// to rescan the front of the string for quotes. Handle the special case of U+301D being
		// closed by either U+301E or U+301F.
		
		XMP_VarString	newItem;
		size_t			splitPoint;
		
		for ( splitPoint = 0; splitPoint <= charOffset; ++splitPoint ) {
			ClassifyCharacter ( item->c_str(), splitPoint, &charKind, &charLen, &uniChar );
			if ( charKind == UCK_quote ) break;
		}
		
		CodePointToUTF8 ( openQuote, newItem );
		newItem.append ( *item, 0, splitPoint );	// Copy the leading "normal" portion.

		for ( charOffset = splitPoint; size_t(charOffset) < item->size(); charOffset += charLen ) {
			ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar );
			newItem.append ( *item, charOffset, charLen );
			if ( (charKind == UCK_quote) && IsSurroundingQuote ( uniChar, openQuote, closeQuote ) ) {
				newItem.append ( *item, charOffset, charLen );
			}
		}
		
		XMP_VarString closeStr;
		CodePointToUTF8 ( closeQuote, closeStr );
		newItem.append ( closeStr );
		
		*item = newItem;
	
	}
	
}	// ApplyQuotes
コード例 #20
0
ファイル: MetadataImpl.cpp プロジェクト: hfiguiere/exempi
	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;
	}
コード例 #21
0
/* class static */ void
XMPUtils::SeparateArrayItems ( XMPMeta *	  xmpObj,
							   XMP_StringPtr  schemaNS,
							   XMP_StringPtr  arrayName,
							   XMP_OptionBits options,
							   XMP_StringPtr  catedStr )
{
	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (catedStr != 0) );	// ! Enforced by wrapper.
	
	XMP_VarString itemValue;
	size_t itemStart, itemEnd;
	size_t nextSize, charSize = 0;	// Avoid VS uninit var warnings.
	UniCharKind	  nextKind, charKind = UCK_normal;
	UniCodePoint  nextChar, uniChar = 0;
	
	// Extract "special" option bits, verify and normalize the others.
	
	bool preserveCommas = false;
	if ( options & kXMPUtil_AllowCommas ) {
		preserveCommas = true;
		options ^= kXMPUtil_AllowCommas;
	}

	options = VerifySetOptions ( options, 0 );	// Keep a zero value, has special meaning below.
	if ( options & ~kXMP_PropArrayFormMask ) XMP_Throw ( "Options can only provide array form", kXMPErr_BadOptions );
	
	// Find the array node, make sure it is OK. Move the current children aside, to be readded later if kept.
	
	XMP_ExpandedXPath arrayPath;
	ExpandXPath ( schemaNS, arrayName, &arrayPath );
	XMP_Node * arrayNode = FindNode ( &xmpObj->tree, arrayPath, kXMP_ExistingOnly );
	
	if ( arrayNode != 0 ) {
		// The array exists, make sure the form is compatible. Zero arrayForm means take what exists.
		XMP_OptionBits arrayForm = arrayNode->options & kXMP_PropArrayFormMask;
		if ( (arrayForm == 0) || (arrayForm & kXMP_PropArrayIsAlternate) ) {
			XMP_Throw ( "Named property must be non-alternate array", kXMPErr_BadXPath );
		}
		if ( (options != 0) && (options != arrayForm) ) XMP_Throw ( "Mismatch of specified and existing array form", kXMPErr_BadXPath );	// *** Right error?
	} else {
		// The array does not exist, try to create it.
		arrayNode = FindNode ( &xmpObj->tree, arrayPath, kXMP_CreateNodes, (options | kXMP_PropValueIsArray) );
		if ( arrayNode == 0 ) XMP_Throw ( "Failed to create named array", kXMPErr_BadXPath );
	}

	XMP_NodeOffspring oldChildren ( arrayNode->children );
	size_t oldChildCount = oldChildren.size();
	arrayNode->children.clear();
	
	// Extract the item values one at a time, until the whole input string is done. Be very careful
	// in the extraction about the string positions. They are essentially byte pointers, while the
	// contents are UTF-8. Adding or subtracting 1 does not necessarily move 1 Unicode character!
	
	size_t endPos = strlen ( catedStr );
	
	itemEnd = 0;
	while ( itemEnd < endPos ) {
		
		// Skip any leading spaces and separation characters. Always skip commas here. They can be
		// kept when within a value, but not when alone between values.
		
		for ( itemStart = itemEnd; itemStart < endPos; itemStart += charSize ) {
			ClassifyCharacter ( catedStr, itemStart, &charKind, &charSize, &uniChar );
			if ( (charKind == UCK_normal) || (charKind == UCK_quote) ) break;
		}
		if ( itemStart >= endPos ) break;
		
		if ( charKind != UCK_quote ) {
		
			// This is not a quoted value. Scan for the end, create an array item from the substring.

			for ( itemEnd = itemStart; itemEnd < endPos; itemEnd += charSize ) {

				ClassifyCharacter ( catedStr, itemEnd, &charKind, &charSize, &uniChar );

				if ( (charKind == UCK_normal) || (charKind == UCK_quote) ) continue;
				if ( (charKind == UCK_comma) && preserveCommas ) continue;
				if ( charKind != UCK_space ) break;

				if ( (itemEnd + charSize) >= endPos ) break;	// Anything left?
				ClassifyCharacter ( catedStr, (itemEnd+charSize), &nextKind, &nextSize, &nextChar );
				if ( (nextKind == UCK_normal) || (nextKind == UCK_quote) ) continue;
				if ( (nextKind == UCK_comma) && preserveCommas ) continue;
				break;	// Have multiple spaces, or a space followed by a separator.

			}		

			itemValue.assign ( catedStr, itemStart, (itemEnd - itemStart) );
		
		} else {
		
			// Accumulate quoted values into a local string, undoubling internal quotes that
			// match the surrounding quotes. Do not undouble "unmatching" quotes.
		
			UniCodePoint openQuote = uniChar;
			UniCodePoint closeQuote = GetClosingQuote ( openQuote );

			itemStart += charSize;	// Skip the opening quote;
			itemValue.erase();
			
			for ( itemEnd = itemStart; itemEnd < endPos; itemEnd += charSize ) {

				ClassifyCharacter ( catedStr, itemEnd, &charKind, &charSize, &uniChar );

				if ( (charKind != UCK_quote) || (! IsSurroundingQuote ( uniChar, openQuote, closeQuote)) ) {
				
					// This is not a matching quote, just append it to the item value.
					itemValue.append ( catedStr, itemEnd, charSize );
					
				} else {
				
					// This is a "matching" quote. Is it doubled, or the final closing quote? Tolerate
					// various edge cases like undoubled opening (non-closing) quotes, or end of input.
					
					if ( (itemEnd + charSize) < endPos ) {
						ClassifyCharacter ( catedStr, itemEnd+charSize, &nextKind, &nextSize, &nextChar );
					} else {
						nextKind = UCK_semicolon; nextSize = 0; nextChar = 0x3B;
					}
					
					if ( uniChar == nextChar ) {
						// This is doubled, copy it and skip the double.
						itemValue.append ( catedStr, itemEnd, charSize );
						itemEnd += nextSize;	// Loop will add in charSize.
					} else if ( ! IsClosingingQuote ( uniChar, openQuote, closeQuote ) ) {
						// This is an undoubled, non-closing quote, copy it.
						itemValue.append ( catedStr, itemEnd, charSize );
					} else {
						// This is an undoubled closing quote, skip it and exit the loop.
						itemEnd += charSize;
						break;
					}

				}

			}	// Loop to accumulate the quoted value.
		
		}

		// Add the separated item to the array. Keep a matching old value in case it had separators.
		
		size_t oldChild;
		for ( oldChild = 0; oldChild < oldChildCount; ++oldChild ) {
			if ( (oldChildren[oldChild] != 0) && (itemValue == oldChildren[oldChild]->value) ) break;
		}
		
		XMP_Node * newItem = 0;
		if ( oldChild == oldChildCount ) {
			newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, itemValue.c_str(), 0 );
		} else {
			newItem = oldChildren[oldChild];
			oldChildren[oldChild] = 0;	// ! Don't match again, let duplicates be seen.
		}
		arrayNode->children.push_back ( newItem );
		
	}	// Loop through all of the returned items.

	// Delete any of the old children that were not kept.
	for ( size_t i = 0; i < oldChildCount; ++i ) {
		if ( oldChildren[i] != 0 ) delete oldChildren[i];
	}
	
}	// SeparateArrayItems