text: // =================================================================================================
  // Copyright 2008 Adobe Systems Incorporated
  // All Rights Reserved.
  //
  // NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
  // of the Adobe license agreement accompanying it.
  // =================================================================================================
  
  /**
  * Tutorial solution for the Walkthrough 3 in the XMP Programmers Guide, Working with custom schema.
  * 
  * Demonstrates how to work with a custom schema that has complex properties. It shows how to access 
  * and modify properties with complex paths using the path composition utilities from the XMP API
  */
  
  #include <string>
  
  // Must be defined to instantiate template classes
  #define TXMP_STRING_TYPE std::string
  
  // Ensure XMP templates are instantiated
  #include "XMP.incl_cpp"
  
  // Provide access to the API
  #include "XMP.hpp"
  
  #include <iostream>
  #include <fstream>
  
  // Made up namespace URI.  Prefix will be xsdkEdit and xsdkUser
  const XMP_StringPtr  kXMP_NS_SDK_EDIT = "http://ns.adobe/meta/sdk/Edit/";
  const XMP_StringPtr  kXMP_NS_SDK_USERS = "http://ns.adobe/meta/sdk/User/";
  
  using namespace std;
  
  /**
  * Client defined callback function to dump XMP to a file.  In this case an output file stream is used
  * to write a buffer, of length bufferSize, to a text file.  This callback is called multiple 
  * times during the DumpObject() operation.  See the XMP API reference for details of 
  * XMP_TextOutputProc() callbacks.
  */
  XMP_Status XMPFileDump(void * refCon, XMP_StringPtr buffer, XMP_StringLen bufferSize)
  {
  	XMP_Status status = 0;
  	try
  	{
  		ofstream * outFile = static_cast<ofstream*>(refCon);
  		(*outFile).write(buffer, bufferSize);
  	}
  	catch(XMP_Error & e)
  	{
  		cout << e.GetErrMsg() << endl;
  		return -1;
  	}
  	return status;
  }
  
  /**
  * Client defined callback function to dump the registered namespaces to a file.  In this case 
  * an output file stream is used to write a buffer, of length bufferSize, to a text file.  This 
  * callback is called multiple times during the DumpObject() operation.  See the XMP API 
  * reference for details of XMP_TextOutputProc() callbacks.
  */
  XMP_Status DumpNS(void * refCon, XMP_StringPtr buffer, XMP_StringLen bufferSize)
  {
  	XMP_Status status = 0;
  
  	try
  	{
  		ofstream *outFile= static_cast<ofstream*>(refCon);
  		(*outFile).write(buffer, bufferSize);
  	}
  	catch(XMP_Error & e)
  	{
  		cout << e.GetErrMsg() << endl;
  		return -1;
  	}
  	return status;
  }
  
  /**
  * Writes an XMP packet in XML format to a text file
  * 
  * rdf - a pointer to the serialized XMP
  * filename - the name of the file to write to
  */
  void writeRDFToFile(string * rdf, string filename)
  {
  	ofstream outFile;
  	outFile.open(filename.c_str(), ios::out);
  	outFile << *rdf;
  	outFile.close();
  }
  
  /**
  * Registers the namespaces that will be used with the custom schema.  Then adds several new
  * properties to that schema.  The properties are complex, containing nested arrays and structures.
  *
  * XMPFiles is not used in this sample, hence no external resource is updated with the metadata. The
  * created XMP object is serialized and written as RDF to a text file, the XMP object is dumped to 
  * a text file and the registered namespaces are also dumped to a text file.*
  *
  */
  int main()
  {
  	if(!SXMPMeta::Initialize())
  	{
  		cout << "Could not initialize Toolkit!";
  	}
  	else
  	{
  		try
  		{
  			// Register the namespaces
  			string actualPrefix;
  			SXMPMeta::RegisterNamespace(kXMP_NS_SDK_EDIT, "xsdkEdit", &actualPrefix);
  			SXMPMeta::RegisterNamespace(kXMP_NS_SDK_USERS, "xsdkUser",&actualPrefix);
  
  			SXMPMeta meta;
  
  			// Adds a user of the document
  			// 1.  Add a new item onto the DocumentUsers array - 
  			// 2.  Compose a path to the last element of DocumentUsers array
  			// 3.  Add a value for the User field of the UserDetails structure
  			// 4.  Add a qualifier to the User field.  Compose the path and set the value
  			// 5.  Add a value for the DUID field of the UserDetails structure
  			// 6.  Add a Contact property for the ContactDetails field of the UserDetails structure
  			// 7.  Compose a path to the ContactDetails field of the UserDetails structure.
  			// 8.  Create the fields of the ContactDetails structure and provide values
  
  			// Create/Append the top level DocumentUsers array.  If the array exists a new item will be added
  			meta.AppendArrayItem(kXMP_NS_SDK_EDIT, "DocumentUsers", kXMP_PropValueIsArray, 0, kXMP_PropValueIsStruct);
  
  			// Compose a path to the last item in the DocumentUsers array, this will point to a UserDetails structure
  			string userItemPath;
  			SXMPUtils::ComposeArrayItemPath(kXMP_NS_SDK_EDIT, "DocumentUsers", kXMP_ArrayLastItem, &userItemPath);
  
  			// We now have a path to the structure, so we can set the field values
  			meta.SetStructField(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "User", "John Smith", 0);
  
  			// Add a qualifier to the User field, first compose the path to the field and then add the qualifier
  			string userFieldPath;
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "User", &userFieldPath);
  			meta.SetQualifier(kXMP_NS_SDK_EDIT, userFieldPath.c_str(), kXMP_NS_SDK_USERS, "Role", "Dev Engineer");
  
  			// Compose a path to the DUID and set field value
  			string duidPath;
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "DUID", &duidPath);
  			meta.SetProperty_Int(kXMP_NS_SDK_EDIT, duidPath.c_str(), 2, 0);
  
  			// Add the ContactDetails field, this field is a Contact structure
  			meta.SetStructField(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "ContactDetails", 0, kXMP_PropValueIsStruct);
  
  			// Compose a path to the field that has the ContactDetails structure
  			string contactStructPath;
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, userItemPath.c_str(), kXMP_NS_SDK_USERS, "ContactDetails", &contactStructPath);
  
  			// Now add the fields - all empty initially
  			meta.SetStructField(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Email", 0, kXMP_PropArrayIsAlternate);	
  			meta.SetStructField(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Telephone", 0, kXMP_PropValueIsArray);
  			meta.SetStructField(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "BaseLocation", "", 0);
  
  			// Add some values for the fields
  			// Email: Get the path to the field named 'Email' in the ContactDetails structure and use it to append items
  			string path;
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Email", &path);
  			meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "js@adobe.meta.com", 0);
  			meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "js@adobe.home.com", 0);
  			
  			// Telephone
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "Telephone", &path);
  			meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "89112", 0);
  			meta.AppendArrayItem(kXMP_NS_SDK_EDIT, path.c_str(), 0, "84432", 0);
  
  			// BaseLocation
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, contactStructPath.c_str(), kXMP_NS_SDK_USERS, "BaseLocation", &path);
  			meta.SetProperty(kXMP_NS_SDK_EDIT, path.c_str(), "London", 0);
  
  			// Add a user edit
  			// 1.  Add an item (a structure) to the DocumentEdit array
  			// 2.  Compose a path to the last item in the DocumentEdit array
  			// 3.  Add fields and values to the EditDetails structure
  
  			// Create the array
  			meta.AppendArrayItem(kXMP_NS_SDK_EDIT, "DocumentEdit", kXMP_PropArrayIsOrdered, 0, kXMP_PropValueIsStruct);
  
  			// Compose a path to the last item of the DocumentEdit array, this gives the path to the structure
  			string lastItemPath;
  			SXMPUtils::ComposeArrayItemPath(kXMP_NS_SDK_EDIT, "DocumentEdit", kXMP_ArrayLastItem, &lastItemPath);
  
  			// Add the Date field
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "EditDate", &path);
  			XMP_DateTime dt;
  			SXMPUtils::CurrentDateTime(&dt);
  			meta.SetProperty_Date(kXMP_NS_SDK_EDIT, path.c_str(), dt, 0);
  
  			// Add the DUID field
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "DUID", &path);
  			meta.SetProperty_Int(kXMP_NS_SDK_EDIT, path.c_str(), 2, 0);
  			
  			// Add the EditComments field
  			SXMPUtils::ComposeStructFieldPath(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "EditComments", &path);
  			meta.SetLocalizedText(kXMP_NS_SDK_EDIT, path.c_str(), "en", "en-US", "Document created.", 0);
  
  			// Add the EditTool field
  			meta.SetStructField(kXMP_NS_SDK_EDIT, lastItemPath.c_str(), kXMP_NS_SDK_EDIT, "EditTool", "FrameXML", 0);
  			
  			// Write the RDF to a file
  			cout << "writing RDF to file CS_RDF.txt" << endl;
  			string metaBuffer;
  			meta.SerializeToBuffer(&metaBuffer);
  			writeRDFToFile(&metaBuffer, "CS_RDF.txt");
  			
  			// Dump the XMP object
  			cout << "dumping XMP object to file XMPDump.txt" << endl;
  			ofstream dumpFile;
  			dumpFile.open("XMPDump.txt", ios::out);
  			meta.DumpObject(XMPFileDump, &dumpFile);
  			dumpFile.close();
  
  			// Dump the namespaces to a file
  			cout << "dumping namespaces to file NameDump.txt" << endl;
  			dumpFile.open("NameDump.txt", ios::out);
  			meta.DumpNamespaces(XMPFileDump, &dumpFile);
  			dumpFile.close();
  			
  		}
  		catch(XMP_Error & e)
  		{
  			cout << "ERROR: " << e.GetErrMsg();
  		}
  
  		SXMPMeta::Terminate();
  	}
  
  	return 0;
  }
  