#539: Make registration of namespaces actually work (Vladimir Nadvornik, S M Ryan)

This commit is contained in:
Andreas Huggel 2008-01-24 14:32:18 +00:00
parent 1b1df1e649
commit 2e3672d211
10 changed files with 122 additions and 21 deletions

View File

@ -54,6 +54,9 @@ set Xmp.tiff.ImageDescription lang=de-DE TIFF Bildbeschreibung
# Register a namespace which Exiv2 doesn't know yet with a prefix. # Register a namespace which Exiv2 doesn't know yet with a prefix.
reg ns myNamespace/ reg ns myNamespace/
# Add a property in the new custom namespace.
set Xmp.ns.myProperty myValue
# There are no built-in Exiv2 value types for structures, qualifiers and # There are no built-in Exiv2 value types for structures, qualifiers and
# nested types. However, these can be added by using an XmpText value and a # nested types. However, these can be added by using an XmpText value and a
# path as the key. # path as the key.
@ -65,7 +68,7 @@ set Xmp.xmpDM.videoFrameSize/stDim:unit inch
# Add an element with a qualifier (using the namespace registered earlier) # Add an element with a qualifier (using the namespace registered earlier)
set Xmp.dc.publisher James Bond set Xmp.dc.publisher James Bond
set Xmp.dc.publisher/?ns:role secret agent set Xmp.dc.publisher[1]/?ns:role secret agent
# Add a qualifer to an array element of Xmp.dc.creator (added above) # Add a qualifer to an array element of Xmp.dc.creator (added above)
set Xmp.dc.creator[2]/?ns:role programmer set Xmp.dc.creator[2]/?ns:role programmer

View File

@ -75,6 +75,10 @@ try {
// image, namespaces are decoded and registered at the same time. // image, namespaces are decoded and registered at the same time.
Exiv2::XmpProperties::registerNs("myNamespace/", "ns"); Exiv2::XmpProperties::registerNs("myNamespace/", "ns");
// -------------------------------------------------------------------------
// Add a property in the new custom namespace.
xmpData["Xmp.ns.myProperty"] = "myValue";
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// There are no specialized values for structures, qualifiers and nested // There are no specialized values for structures, qualifiers and nested
// types. However, these can be added by using an XmpTextValue and a path as // types. However, these can be added by using an XmpTextValue and a path as

View File

@ -1413,7 +1413,7 @@ namespace Action {
std::cout << _("Reg ") << modifyCmd.key_ << "=\"" std::cout << _("Reg ") << modifyCmd.key_ << "=\""
<< modifyCmd.value_ << "\"" << std::endl; << modifyCmd.value_ << "\"" << std::endl;
} }
Exiv2::XmpProperties::registerNs(modifyCmd.value_, modifyCmd.key_); // Registration has been done immediately after parsing the command.
} }
Modify::AutoPtr Modify::clone() const Modify::AutoPtr Modify::clone() const

View File

@ -1015,6 +1015,12 @@ namespace {
modifyCmd.explicitType_ = explicitType; modifyCmd.explicitType_ = explicitType;
modifyCmd.value_ = value; modifyCmd.value_ = value;
if (cmdId == reg) {
// Registration needs to be done immediately as the new namespaces are
// looked up during parsing of subsequent lines (to validate XMP keys).
Exiv2::XmpProperties::registerNs(modifyCmd.value_, modifyCmd.key_);
}
return true; return true;
} // parseLine } // parseLine

View File

@ -43,6 +43,8 @@ EXIV2_RCSID("@(#) $Id$")
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <cstring>
#include <cstdlib>
// ***************************************************************************** // *****************************************************************************
// class member definitions // class member definitions
@ -812,16 +814,57 @@ namespace Exiv2 {
XmpProperties::NsRegistry XmpProperties::nsRegistry_; XmpProperties::NsRegistry XmpProperties::nsRegistry_;
const XmpNsInfo* XmpProperties::lookupNsRegistry(const XmpNsInfo::Prefix& prefix)
{
for (NsRegistry::const_iterator i = nsRegistry_.begin();
i != nsRegistry_.end(); ++i) {
if (i->second == prefix) return &(i->second);
}
return 0;
}
void XmpProperties::registerNs(const std::string& ns, void XmpProperties::registerNs(const std::string& ns,
const std::string& prefix) const std::string& prefix)
{ {
std::string ns2 = ns; std::string ns2 = ns;
if ( ns2.substr(ns2.size() - 1, 1) != "/" if ( ns2.substr(ns2.size() - 1, 1) != "/"
&& ns2.substr(ns2.size() - 1, 1) != "#") ns2 += "/"; && ns2.substr(ns2.size() - 1, 1) != "#") ns2 += "/";
nsRegistry_[ns2] = prefix; // Allocated memory is freed when the namespace is unregistered.
// Using malloc/free for better system compatibility in case
// users don't unregister their namespaces explicitely.
XmpNsInfo xn;
char* c = static_cast<char*>(std::malloc(ns2.size() + 1));
std::strcpy(c, ns2.c_str());
xn.ns_ = c;
c = static_cast<char*>(std::malloc(prefix.size() + 1));
std::strcpy(c, prefix.c_str());
xn.prefix_ = c;
xn.xmpPropertyInfo_ = 0;
xn.desc_ = "";
nsRegistry_[ns2] = xn;
XmpParser::registerNs(ns2, prefix); XmpParser::registerNs(ns2, prefix);
} }
void XmpProperties::unregisterNs(const std::string& ns)
{
NsRegistry::iterator i = nsRegistry_.find(ns);
if (i != nsRegistry_.end()) {
XmpParser::unregisterNs(ns);
std::free(const_cast<char*>(i->second.prefix_));
std::free(const_cast<char*>(i->second.ns_));
nsRegistry_.erase(i);
}
}
void XmpProperties::unregisterNs()
{
NsRegistry::iterator i = nsRegistry_.begin();
while (i != nsRegistry_.end()) {
NsRegistry::iterator kill = i++;
unregisterNs(kill->first);
}
}
std::string XmpProperties::prefix(const std::string& ns) std::string XmpProperties::prefix(const std::string& ns)
{ {
std::string ns2 = ns; std::string ns2 = ns;
@ -829,7 +872,7 @@ namespace Exiv2 {
NsRegistry::const_iterator i = nsRegistry_.find(ns2); NsRegistry::const_iterator i = nsRegistry_.find(ns2);
std::string p; std::string p;
if (i != nsRegistry_.end()) { if (i != nsRegistry_.end()) {
p = i->second; p = i->second.prefix_;
} }
else { else {
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Ns(ns2)); const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Ns(ns2));
@ -840,13 +883,8 @@ namespace Exiv2 {
std::string XmpProperties::ns(const std::string& prefix) std::string XmpProperties::ns(const std::string& prefix)
{ {
std::string n; const XmpNsInfo* xn = lookupNsRegistry(XmpNsInfo::Prefix(prefix));
for (NsRegistry::const_iterator i = nsRegistry_.begin(); if (xn != 0) return xn->ns_;
i != nsRegistry_.end(); ++i) {
if (i->second == prefix) {
return i->first;
}
}
return nsInfo(prefix)->ns_; return nsInfo(prefix)->ns_;
} }
@ -894,7 +932,9 @@ namespace Exiv2 {
const XmpNsInfo* XmpProperties::nsInfo(const std::string& prefix) const XmpNsInfo* XmpProperties::nsInfo(const std::string& prefix)
{ {
const XmpNsInfo* xn = find(xmpNsInfo, XmpNsInfo::Prefix(prefix)); const XmpNsInfo::Prefix pf(prefix);
const XmpNsInfo* xn = lookupNsRegistry(pf);
if (!xn) xn = find(xmpNsInfo, pf);
if (!xn) throw Error(35, prefix); if (!xn) throw Error(35, prefix);
return xn; return xn;
} }

View File

@ -181,13 +181,33 @@ namespace Exiv2 {
@brief Register namespace \em ns with preferred prefix \em prefix. @brief Register namespace \em ns with preferred prefix \em prefix.
If the namespace is a known or previously registered namespace, the If the namespace is a known or previously registered namespace, the
prefix is overwritten. This also invalidates XMP keys generated with prefix is overwritten.
the previous prefix.
@note This invalidates XMP keys generated with the previous prefix.
*/ */
static void registerNs(const std::string& ns, const std::string& prefix); static void registerNs(const std::string& ns, const std::string& prefix);
/*!
@brief Unregister a custom namespace \em ns.
The function only has an effect if there is a namespace \em ns
registered earlier, it does not unregister built-in namespaces.
@note This invalidates XMP keys generated in this namespace.
*/
static void unregisterNs(const std::string& ns);
/*!
@brief Unregister all custom namespaces.
The function only unregisters namespaces registered earlier, it does not
unregister built-in namespaces.
@note This invalidates XMP keys generated in any custom namespace.
*/
static void unregisterNs();
private: private:
typedef std::map<std::string, std::string> NsRegistry; typedef std::map<std::string, XmpNsInfo> NsRegistry;
static const XmpNsInfo* lookupNsRegistry(const XmpNsInfo::Prefix& prefix);
static NsRegistry nsRegistry_; static NsRegistry nsRegistry_;
}; // class XmpProperties }; // class XmpProperties

View File

@ -371,6 +371,7 @@ namespace Exiv2 {
void XmpParser::terminate() void XmpParser::terminate()
{ {
XmpProperties::unregisterNs();
if (initialized_) { if (initialized_) {
#ifdef EXV_HAVE_XMP_TOOLKIT #ifdef EXV_HAVE_XMP_TOOLKIT
SXMPMeta::Terminate(); SXMPMeta::Terminate();
@ -394,7 +395,20 @@ namespace Exiv2 {
initialize(); initialize();
return true; return true;
#endif #endif
} // XmpParser::registerNs } // XmpParser::registerNs
void XmpParser::unregisterNs(const std::string& ns)
{
#ifdef EXV_HAVE_XMP_TOOLKIT
try {
// Throws XMP Toolkit error 8: Unimplemented method XMPMeta::DeleteNamespace
// SXMPMeta::DeleteNamespace(ns.c_str());
}
catch (const XMP_Error& e) {
throw Error(40, e.GetID(), e.GetErrMsg());
}
#endif
} // XmpParser::unregisterNs
#ifdef EXV_HAVE_XMP_TOOLKIT #ifdef EXV_HAVE_XMP_TOOLKIT
int XmpParser::decode( XmpData& xmpData, int XmpParser::decode( XmpData& xmpData,

View File

@ -257,6 +257,7 @@ namespace Exiv2 {
*/ */
class XmpParser { class XmpParser {
friend void XmpProperties::registerNs(const std::string&, const std::string&); friend void XmpProperties::registerNs(const std::string&, const std::string&);
friend void XmpProperties::unregisterNs(const std::string&);
public: public:
/*! /*!
@brief Decode XMP metadata from an XMP packet \em xmpPacket into @brief Decode XMP metadata from an XMP packet \em xmpPacket into
@ -299,7 +300,7 @@ namespace Exiv2 {
*/ */
static bool initialize(); static bool initialize();
/*! /*!
@brief Terminate the XMP Toolkit. @brief Terminate the XMP Toolkit and unregister custom namespaces.
Call this method when the XmpParser is no longer needed to Call this method when the XmpParser is no longer needed to
allow the XMP Toolkit to cleanly shutdown. allow the XMP Toolkit to cleanly shutdown.
@ -316,6 +317,12 @@ namespace Exiv2 {
*/ */
static bool registerNs(const std::string& ns, static bool registerNs(const std::string& ns,
const std::string& prefix); const std::string& prefix);
/*!
@brief Delete a namespace from the XMP Toolkit.
XmpProperties::unregisterNs calls this to synchronize namespaces.
*/
static void unregisterNs(const std::string& ns);
// DATA // DATA
static bool initialized_; //! Indicates if the XMP Toolkit has been initialized static bool initialized_; //! Indicates if the XMP Toolkit has been initialized

View File

@ -54,6 +54,9 @@ set Xmp.tiff.ImageDescription lang=de-DE TIFF Bildbeschreibung
# Register a namespace which Exiv2 doesn't know yet with a prefix. # Register a namespace which Exiv2 doesn't know yet with a prefix.
reg ns myNamespace/ reg ns myNamespace/
# Add a property in the new custom namespace.
set Xmp.ns.myProperty myValue
# There are no built-in Exiv2 value types for structures, qualifiers and # There are no built-in Exiv2 value types for structures, qualifiers and
# nested types. However, these can be added by using an XmpText value and a # nested types. However, these can be added by using an XmpText value and a
# path as the key. # path as the key.
@ -65,7 +68,7 @@ set Xmp.xmpDM.videoFrameSize/stDim:unit inch
# Add an element with a qualifier (using the namespace registered earlier) # Add an element with a qualifier (using the namespace registered earlier)
set Xmp.dc.publisher James Bond set Xmp.dc.publisher James Bond
set Xmp.dc.publisher/?ns:role secret agent set Xmp.dc.publisher[1]/?ns:role secret agent
# Add a qualifer to an array element of Xmp.dc.creator (added above) # Add a qualifer to an array element of Xmp.dc.creator (added above)
set Xmp.dc.creator[2]/?ns:role programmer set Xmp.dc.creator[2]/?ns:role programmer

View File

@ -293,6 +293,7 @@ Xmp.dc.format XmpText 10 image/jpeg
Xmp.dc.creator XmpSeq 3 1) The first creator, 2) The second creator, 3) And another one Xmp.dc.creator XmpSeq 3 1) The first creator, 2) The second creator, 3) And another one
Xmp.dc.description LangAlt 2 lang="x-default" Hello, World, lang="de-DE" Hallo, Welt Xmp.dc.description LangAlt 2 lang="x-default" Hello, World, lang="de-DE" Hallo, Welt
Xmp.tiff.ImageDescription LangAlt 2 lang="x-default" TIFF image description, lang="de-DE" TIFF Bildbeschreibung Xmp.tiff.ImageDescription LangAlt 2 lang="x-default" TIFF image description, lang="de-DE" TIFF Bildbeschreibung
Xmp.ns.myProperty XmpText 7 myValue
Xmp.xmpDM.videoFrameSize/stDim:w XmpText 2 16 Xmp.xmpDM.videoFrameSize/stDim:w XmpText 2 16
Xmp.xmpDM.videoFrameSize/stDim:h XmpText 1 9 Xmp.xmpDM.videoFrameSize/stDim:h XmpText 1 9
Xmp.xmpDM.videoFrameSize/stDim:unit XmpText 4 inch Xmp.xmpDM.videoFrameSize/stDim:unit XmpText 4 inch
@ -323,7 +324,8 @@ Xmp.xmpBJ.JobRef[2]/stJob:role XmpText 8 Best man
dc:five="256" dc:five="256"
dc:six="false" dc:six="false"
dc:seven="Seven" dc:seven="Seven"
dc:format="image/jpeg"> dc:format="image/jpeg"
ns:myProperty="myValue">
<dc:subject> <dc:subject>
<rdf:Bag> <rdf:Bag>
<rdf:li>Palmtree</rdf:li> <rdf:li>Palmtree</rdf:li>
@ -427,11 +429,12 @@ Set Xmp.dc.description "Hello, World" (LangAlt)
Set Xmp.tiff.ImageDescription "TIFF image description" (LangAlt) Set Xmp.tiff.ImageDescription "TIFF image description" (LangAlt)
Set Xmp.tiff.ImageDescription "lang=de-DE TIFF Bildbeschreibung" (LangAlt) Set Xmp.tiff.ImageDescription "lang=de-DE TIFF Bildbeschreibung" (LangAlt)
Reg ns="myNamespace/" Reg ns="myNamespace/"
Set Xmp.ns.myProperty "myValue" (XmpText)
Set Xmp.xmpDM.videoFrameSize/stDim:w "16" (XmpText) Set Xmp.xmpDM.videoFrameSize/stDim:w "16" (XmpText)
Set Xmp.xmpDM.videoFrameSize/stDim:h "9" (XmpText) Set Xmp.xmpDM.videoFrameSize/stDim:h "9" (XmpText)
Set Xmp.xmpDM.videoFrameSize/stDim:unit "inch" (XmpText) Set Xmp.xmpDM.videoFrameSize/stDim:unit "inch" (XmpText)
Set Xmp.dc.publisher "James Bond" (XmpBag) Set Xmp.dc.publisher "James Bond" (XmpBag)
Set Xmp.dc.publisher/?ns:role "secret agent" (XmpText) Set Xmp.dc.publisher[1]/?ns:role "secret agent" (XmpText)
Set Xmp.dc.creator[2]/?ns:role "programmer" (XmpText) Set Xmp.dc.creator[2]/?ns:role "programmer" (XmpText)
Set Xmp.xmpBJ.JobRef "type=Bag" (XmpText) Set Xmp.xmpBJ.JobRef "type=Bag" (XmpText)
Set Xmp.xmpBJ.JobRef[1]/stJob:name "Birthday party" (XmpText) Set Xmp.xmpBJ.JobRef[1]/stJob:name "Birthday party" (XmpText)
@ -457,8 +460,9 @@ Xmp.dc.creator[2]/?ns:role XmpText 10 programmer
Xmp.dc.creator[3] XmpText 18 3) And another one Xmp.dc.creator[3] XmpText 18 3) And another one
Xmp.dc.description LangAlt 2 lang="x-default" Hello, World, lang="de-DE" Hallo, Welt Xmp.dc.description LangAlt 2 lang="x-default" Hello, World, lang="de-DE" Hallo, Welt
Xmp.dc.publisher XmpText 0 type="Bag" Xmp.dc.publisher XmpText 0 type="Bag"
Xmp.dc.publisher/?ns:role XmpText 12 secret agent
Xmp.dc.publisher[1] XmpText 10 James Bond Xmp.dc.publisher[1] XmpText 10 James Bond
Xmp.dc.publisher[1]/?ns:role XmpText 12 secret agent
Xmp.ns.myProperty XmpText 7 myValue
Xmp.tiff.ImageDescription LangAlt 2 lang="x-default" TIFF image description, lang="de-DE" TIFF Bildbeschreibung Xmp.tiff.ImageDescription LangAlt 2 lang="x-default" TIFF image description, lang="de-DE" TIFF Bildbeschreibung
Xmp.xmpDM.videoFrameSize XmpText 0 type="Struct" Xmp.xmpDM.videoFrameSize XmpText 0 type="Struct"
Xmp.xmpDM.videoFrameSize/stDim:w XmpText 2 16 Xmp.xmpDM.videoFrameSize/stDim:w XmpText 2 16