Exemplo n.º 1
0
	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;
		}
	}
// =================================================================================================
// PackageFormat_Support::AddResourceIfExists
// ================================
bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & folderPath,
	XMP_StringPtr prefix, XMP_StringPtr postfix )
{
	Host_IO::FolderRef folderHandle = Host_IO::OpenFolder ( folderPath.c_str() );
	if ( folderHandle == Host_IO::noFolderRef || !prefix || !postfix )
		return false;// can't open folder.
	XMP_VarString fileName, filePath;
	size_t fileNameLength;
	size_t prefixLength = strlen ( prefix );
	size_t postfixLength = strlen ( postfix );
	bool atleastOneFileAdded = false;
	while ( Host_IO::GetNextChild ( folderHandle, &fileName ) )
	{
		fileNameLength = fileName.length();
		// Check if the file name starts with prefix and ends with postfix
		if ( fileNameLength >= ( prefixLength + postfixLength ) && 
			fileName.compare ( fileNameLength-postfixLength, postfixLength, postfix ) == 0 && 
			fileName.compare ( 0, prefixLength, prefix ) == 0)
		{
			filePath = folderPath + kDirChar + fileName;
			PackageFormat_Support::AddResourceIfExists ( resourceList, filePath );
			atleastOneFileAdded = true;
		}
	}
	// close folder
	Host_IO::CloseFolder ( folderHandle );
	return atleastOneFileAdded;
}	// PackageFormat_Support::AddResourceIfExists
Exemplo n.º 3
0
	IterNode ( XMP_OptionBits _options, const XMP_VarString& _fullPath, size_t _leafOffset )
			 : options(_options), fullPath(_fullPath), leafOffset(_leafOffset), visitStage(kIter_BeforeVisit)
	{
		#if 0	// *** XMP_DebugBuild
			_pathPtr = fullPath.c_str();
			_leafPtr = _pathPtr + leafOffset;
		#endif
	};
bool PackageFormat_Support::AddResourceIfExists ( XMP_StringVector * resourceList, const XMP_VarString & file )
{
	if ( Host_IO::Exists ( file.c_str() ) ) {
		resourceList->push_back ( file );
		return true;
	}
	return false;
}	// PackageFormat_Support::AddResourceIfExists
Exemplo n.º 5
0
bool
XMPMeta::GetLocalizedText ( XMP_StringPtr	 schemaNS,
							XMP_StringPtr	 arrayName,
							XMP_StringPtr	 _genericLang,
							XMP_StringPtr	 _specificLang,
							XMP_StringPtr *	 actualLang,
							XMP_StringLen *	 langSize,
							XMP_StringPtr *	 itemValue,
							XMP_StringLen *	 valueSize,
							XMP_OptionBits * options ) const
{
	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) );	// Enforced by wrapper.
	XMP_Assert ( (actualLang != 0) && (langSize != 0) );	// Enforced by wrapper.
	XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 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 );
	
	const XMP_Node * arrayNode = FindConstNode ( &tree, arrayPath );	// *** This expand/find idiom is used in 3 Getters.
	if ( arrayNode == 0 ) return false;			// *** Should extract it into a local utility.
	
	XMP_CLTMatch match;
	const XMP_Node * itemNode;
	
	match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &itemNode );
	if ( match == kXMP_CLT_NoValues ) return false;
	
	*actualLang = itemNode->qualifiers[0]->value.c_str();
	*langSize   = static_cast<XMP_Index>( itemNode->qualifiers[0]->value.size() );
	*itemValue  = itemNode->value.c_str();
	*valueSize  = static_cast<XMP_Index>( itemNode->value.size() );
	*options    = itemNode->options;

	return true;
	
}	// GetLocalizedText
Exemplo n.º 6
0
static void
DeclareElemNamespace ( const XMP_VarString & elemName,
					   XMP_VarString &		 usedNS,
					   XMP_VarString &		 outputStr,
					   XMP_StringPtr		 newline,
					   XMP_StringPtr		 indentStr,
					   XMP_Index			 indent )
{
	size_t colonPos = elemName.find ( ':' );

	if ( colonPos != XMP_VarString::npos ) {
		XMP_VarString nsPrefix ( elemName.substr ( 0, colonPos+1 ) );
		XMP_StringPtr nsURI;
		bool nsFound = sRegisteredNamespaces->GetURI ( nsPrefix.c_str(), &nsURI, 0 );
		XMP_Enforce ( nsFound );
		DeclareOneNamespace ( nsPrefix.c_str(), nsURI, usedNS, outputStr, newline, indentStr, indent );
	}

}	// DeclareElemNamespace
Exemplo n.º 7
0
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 += "&quot;";
				} else if ( ch == '<' ) {
					outputStr += "&lt;";
				} else if ( ch == '>' ) {
					outputStr += "&gt;";
				} else {
					XMP_Assert ( ch == '&' );
					outputStr += "&amp;";
				}

			}

			++runEnd;

		}
		
		runStart = runEnd;
	
	}

}	// AppendNodeValue
Exemplo n.º 8
0
void
XMPMeta::DeleteArrayItem ( XMP_StringPtr schemaNS,
						   XMP_StringPtr arrayName,
						   XMP_Index	 itemIndex )
{
	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.

	XMP_VarString itemPath;
	XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
	DeleteProperty ( schemaNS, itemPath.c_str() );

}	// DeleteArrayItem
Exemplo n.º 9
0
bool
XMPMeta::DoesArrayItemExist	( XMP_StringPtr	schemaNS,
							  XMP_StringPtr	arrayName,
							  XMP_Index		itemIndex ) const
{
	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.

	XMP_VarString itemPath;
	XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
	return DoesPropertyExist ( schemaNS, itemPath.c_str() );

}	// DoesArrayItemExist
Exemplo n.º 10
0
bool
XMPMeta::DoesQualifierExist	( XMP_StringPtr	schemaNS,
							  XMP_StringPtr	propName,
							  XMP_StringPtr	qualNS,
							  XMP_StringPtr	qualName ) const
{
	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.

	XMP_VarString qualPath;
	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
	return DoesPropertyExist ( schemaNS, qualPath.c_str() );

}	// DoesQualifierExist
Exemplo n.º 11
0
bool
XMPMeta::DoesStructFieldExist ( XMP_StringPtr schemaNS,
								XMP_StringPtr structName,
								XMP_StringPtr fieldNS,
								XMP_StringPtr fieldName ) const
{
	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.

	XMP_VarString fieldPath;
	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
	return DoesPropertyExist ( schemaNS, fieldPath.c_str() );

}	// DoesStructFieldExist
Exemplo n.º 12
0
void
XMPMeta::DeleteQualifier ( XMP_StringPtr schemaNS,
						   XMP_StringPtr propName,
						   XMP_StringPtr qualNS,
						   XMP_StringPtr qualName )
{
	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.

	XMP_VarString qualPath;
	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
	DeleteProperty ( schemaNS, qualPath.c_str() );

}	// DeleteQualifier
Exemplo n.º 13
0
void
XMPMeta::DeleteStructField ( XMP_StringPtr schemaNS,
							 XMP_StringPtr structName,
							 XMP_StringPtr fieldNS,
							 XMP_StringPtr fieldName )
{
	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.

	XMP_VarString fieldPath;
	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
	DeleteProperty ( schemaNS, fieldPath.c_str() );

}	// DeleteStructField
Exemplo n.º 14
0
void
XMPMeta::SetProperty_Date ( XMP_StringPtr		   schemaNS,
							XMP_StringPtr		   propName,
							const	XMP_DateTime & propValue,
							XMP_OptionBits		   options )
{
	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.

	XMP_VarString valueStr;
	XMPUtils::ConvertFromDate ( propValue, &valueStr );
	SetProperty ( schemaNS, propName, valueStr.c_str(), options );
	
}	// SetProperty_Date
Exemplo n.º 15
0
void
XMPMeta::SetProperty_Float ( XMP_StringPtr	schemaNS,
							 XMP_StringPtr	propName,
							 double			propValue,
							 XMP_OptionBits	options )
{
	XMP_Assert ( (schemaNS != 0) && (propName != 0) );	// Enforced by wrapper.

	XMP_VarString valueStr;
	XMPUtils::ConvertFromFloat ( propValue, "", &valueStr );
	SetProperty ( schemaNS, propName, valueStr.c_str(), options );
	
}	// SetProperty_Float
Exemplo n.º 16
0
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
Exemplo n.º 17
0
void
XMPMeta::SetStructField	( XMP_StringPtr	 schemaNS,
						  XMP_StringPtr	 structName,
						  XMP_StringPtr	 fieldNS,
						  XMP_StringPtr	 fieldName,
						  XMP_StringPtr	 fieldValue,
						  XMP_OptionBits options )
{
	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.

	XMP_VarString fieldPath;
	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
	SetProperty ( schemaNS, fieldPath.c_str(), fieldValue, options );

}	// SetStructField
Exemplo n.º 18
0
bool
XMPMeta::GetStructField	( XMP_StringPtr	   schemaNS,
						  XMP_StringPtr	   structName,
						  XMP_StringPtr	   fieldNS,
						  XMP_StringPtr	   fieldName,
						  XMP_StringPtr *  fieldValue,
						  XMP_StringLen *  valueSize,
						  XMP_OptionBits * options ) const
{
	XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) );	// Enforced by wrapper.
	XMP_Assert ( (fieldValue != 0) && (options != 0) );	// Enforced by wrapper.

	XMP_VarString fieldPath;
	XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath );
	return GetProperty ( schemaNS, fieldPath.c_str(), fieldValue, valueSize, options );

}	// GetStructField
Exemplo n.º 19
0
bool
XMPMeta::GetQualifier ( XMP_StringPtr	 schemaNS,
						XMP_StringPtr	 propName,
						XMP_StringPtr	 qualNS,
						XMP_StringPtr	 qualName,
						XMP_StringPtr *	 qualValue,
						XMP_StringLen *  valueSize,
						XMP_OptionBits * options ) const
{
	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.
	XMP_Assert ( (qualValue != 0) && (options != 0) );	// Enforced by wrapper.

	XMP_VarString qualPath;
	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
	return GetProperty ( schemaNS, qualPath.c_str(), qualValue, valueSize, options );

}	// GetQualifier
Exemplo n.º 20
0
static void
VerifyQualName ( XMP_StringPtr qualName, XMP_StringPtr nameEnd )
{
	if ( qualName >= nameEnd ) XMP_Throw ( "Empty qualified name", kXMPErr_BadXPath );

	XMP_StringPtr colonPos = qualName;
	while ( (colonPos < nameEnd) && (*colonPos != ':') ) ++colonPos;
	if ( (colonPos == qualName) || (colonPos >= nameEnd) ) XMP_Throw ( "Ill-formed qualified name", kXMPErr_BadXPath );

	VerifySimpleXMLName ( qualName, colonPos );
	VerifySimpleXMLName ( colonPos+1, nameEnd );

	size_t prefixLen = colonPos - qualName + 1;	// ! Include the colon.
	XMP_VarString prefix ( qualName, prefixLen );
	bool nsFound = sRegisteredNamespaces->GetURI ( prefix.c_str(), 0, 0 );
	if ( ! nsFound ) XMP_Throw ( "Unknown namespace prefix for qualified name", kXMPErr_BadXPath );

}	// VerifyQualName
Exemplo n.º 21
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
Exemplo n.º 22
0
bool
XMPMeta::GetArrayItem ( XMP_StringPtr	 schemaNS,
						XMP_StringPtr	 arrayName,
						XMP_Index		 itemIndex,
						XMP_StringPtr *	 itemValue,
						XMP_StringLen *  valueSize,
						XMP_OptionBits * options ) const
{
	XMP_Assert ( (schemaNS != 0) && (arrayName != 0) );	// Enforced by wrapper.
	XMP_Assert ( (itemValue != 0) && (options != 0) );	// Enforced by wrapper.

	// ! Special case check to make errors consistent if the array does not exist. The other array
	// ! functions and existing array here (empty or not) already throw.
	if ( (itemIndex <= 0) && (itemIndex != kXMP_ArrayLastItem) ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath );

	XMP_VarString itemPath;
	XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath );
	return GetProperty ( schemaNS, itemPath.c_str(), itemValue, valueSize, options );

}	// GetArrayItem
Exemplo n.º 23
0
void
XMPMeta::SetQualifier ( XMP_StringPtr  schemaNS,
						XMP_StringPtr  propName,
						XMP_StringPtr  qualNS,
						XMP_StringPtr  qualName,
						XMP_StringPtr  qualValue,
						XMP_OptionBits options )
{
	XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) );	// Enforced by wrapper.

	XMP_ExpandedXPath expPath;
	ExpandXPath ( schemaNS, propName, &expPath );
	XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly );
	if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath );

	XMP_VarString qualPath;
	XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath );
	SetProperty ( schemaNS, qualPath.c_str(), qualValue, options );

}	// SetQualifier
Exemplo n.º 24
0
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
Exemplo n.º 25
0
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
Exemplo n.º 26
0
	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;
	}
Exemplo n.º 27
0
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
Exemplo n.º 28
0
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
Exemplo n.º 29
0
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
Exemplo n.º 30
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