PropertyList.cpp
上传用户:xqtpzdz
上传日期:2022-05-21
资源大小:1764k
文件大小:21k
源码类别:

xml/soap/webservice

开发平台:

Visual C++

  1. /****************License************************************************
  2.  * Vocalocity OpenVXI
  3.  * Copyright (C) 2004-2005 by Vocalocity, Inc. All Rights Reserved.
  4.  * This program is free software; you can redistribute it and/or
  5.  * modify it under the terms of the GNU General Public License
  6.  * as published by the Free Software Foundation; either version 2
  7.  * of the License, or (at your option) any later version.
  8.  *  
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  17.  * Vocalocity, the Vocalocity logo, and VocalOS are trademarks or 
  18.  * registered trademarks of Vocalocity, Inc. 
  19.  * OpenVXI is a trademark of Scansoft, Inc. and used under license 
  20.  * by Vocalocity.
  21.  ***********************************************************************/
  22. #include "PropertyList.hpp"
  23. #include "CommonExceptions.hpp"       // for VXIException class
  24. #include "VXIinet.h"
  25. #include "VXML.h"
  26. #include "SimpleLogger.hpp"           // for SimpleLogger
  27. #include "DocumentParser.hpp"         // for string constants
  28. #include "DocumentModel.hpp"
  29. #include <sstream>
  30. // ------*---------*---------*---------*---------*---------*---------*---------
  31. // The use of spaces here creates identifiers which cannot be specified
  32. // within the property or meta tags.
  33. const VXIchar * const PropertyList::BaseURI     = L" base";
  34. const VXIchar * const PropertyList::Language    = L" lang";
  35. const VXIchar * const PropertyList::AbsoluteURI = L" absoluteURI";
  36. const VXIchar * const PropertyList::SourceEncoding = L" encoding";
  37. static vxistring toString(const VXIString * s)
  38. {
  39.   if (s == NULL) return L"";
  40.   const VXIchar * temp = VXIStringCStr(s);
  41.   if (temp == NULL) return L"";
  42.   return temp;
  43. }
  44. static vxistring toString(const VXIchar * s)
  45. {
  46.   if (s == NULL) return L"";
  47.   return s;
  48. }
  49. // ------*---------*---------*---------*---------*---------*---------*---------
  50. PropertyList::PropertyList(const SimpleLogger & l) : log(l)
  51. {
  52.   properties.reserve(LAST_PROP);
  53.   while (properties.size() < LAST_PROP)
  54.     properties.push_back(STRINGMAP());
  55. }
  56. PropertyList::PropertyList(const PropertyList & p) : log(p.log)
  57. {
  58.   properties = p.properties;
  59. }
  60. PropertyList& PropertyList::operator=(const PropertyList & x)
  61. {
  62.   if (this != &x) {
  63.     properties = x.properties;
  64.   }
  65.   return *this;
  66. }
  67. const VXIchar* PropertyList::GetProperty(const VXIchar * key,
  68.                                          PropertyLevel L) const
  69. {
  70.   int pos = L;
  71.   do {
  72.     if (pos == LAST_PROP) --pos;
  73.     STRINGMAP::const_iterator i = properties[pos].find(key);
  74.     if (i != properties[pos].end()) return (*i).second.c_str();
  75.   } while (--pos >= 0);
  76.   return NULL;
  77. }
  78. bool PropertyList::SetProperty(const VXIchar * key, const VXIchar * value,
  79.                                PropertyLevel level)
  80. {
  81.   if (level == LAST_PROP || key == NULL || value == NULL) return false;
  82.   STRINGMAP & propmap = properties[level];
  83.   propmap[key] = value;
  84.   return true;
  85. }
  86. void PropertyList::SetProperties(const VXMLElement & doc, PropertyLevel level,
  87.                                  const VXIMapHolder & docProps)
  88. {
  89.   // (0) Check the arguments
  90.   if (doc == 0) return;
  91.   if (properties.size() != LAST_PROP) {
  92.     log.LogError(999, SimpleLogger::MESSAGE,
  93.                  L"internal property list corrupted");
  94.     throw VXIException::Fatal();
  95.   }
  96.   // (1) Clear all properties at this level and above.
  97.   properties[level].clear();
  98.   STRINGMAP & propmap = properties[level];
  99.   // (2) Add document properties.
  100.   if (docProps.GetValue() != NULL) {
  101.     const VXIValue * val;
  102.     const VXIString * str;
  103.     // (2.1) Handle document base URI.
  104.     val = VXIMapGetProperty(docProps.GetValue(), PropertyList::BaseURI);
  105.     str = reinterpret_cast<const VXIString *>(val);
  106.     if (VXIStringLength(str) != 0)
  107.       propmap.insert(propmap.end(),
  108.                      STRINGMAP::value_type(PropertyList::BaseURI,
  109.                                            toString(str)));
  110.     // (2.2) Handle document language.  If the document property is not
  111.     // specified, the value of the xml:lang property will be used.
  112.     val = VXIMapGetProperty(docProps.GetValue(), PropertyList::Language);
  113.     str = reinterpret_cast<const VXIString *>(val);
  114.     if (VXIStringLength(str) != 0)
  115.       propmap.insert(propmap.end(),
  116.                      STRINGMAP::value_type(PropertyList::Language,
  117.                                            toString(str)));
  118.     else {
  119.       const VXIchar * lang = GetProperty(L"xml:lang");
  120.       if (lang != NULL)
  121.         propmap.insert(propmap.end(),
  122.                        STRINGMAP::value_type(PropertyList::Language,
  123.                                              toString(lang)));
  124.     }
  125.     
  126.     // (2.3) Handle document absolute URI.
  127.     val = VXIMapGetProperty(docProps.GetValue(), PropertyList::AbsoluteURI);
  128.     str = reinterpret_cast<const VXIString *>(val);
  129.     if (VXIStringLength(str) != 0)
  130.       propmap.insert(propmap.end(),
  131.                      STRINGMAP::value_type(PropertyList::AbsoluteURI,
  132.                                            toString(str)));
  133. // (2.4) Handle document encoding.
  134.     val = VXIMapGetProperty(docProps.GetValue(), PropertyList::SourceEncoding);
  135.     str = reinterpret_cast<const VXIString *>(val);
  136.     if (VXIStringLength(str) != 0)
  137.       propmap.insert(propmap.end(),
  138.                      STRINGMAP::value_type(PropertyList::SourceEncoding,
  139.                                            toString(str)));
  140.   }
  141.   // (3) Walk through this level.  Find all <property> nodes and set the
  142.   // corresponding values.
  143.   for (VXMLNodeIterator it(doc); it; ++it) {
  144.     VXMLNode child = *it;
  145.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  146.     const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  147.     VXMLElementType nodeName = elem.GetName();
  148.     if (nodeName == NODE_PROPERTY) {
  149.       vxistring name;
  150.       vxistring value;
  151.       elem.GetAttribute(ATTRIBUTE_NAME, name);
  152.       elem.GetAttribute(ATTRIBUTE_VALUE, value);
  153.       
  154.       // This added the name / value pair, converting to vxistrings.
  155.   propmap[name] = value;
  156.     }
  157.   }
  158. }
  159. //****************************************************************
  160. //* Fetchobj building routines
  161. //****************************************************************
  162. bool PropertyList::ConvertTimeToMilliseconds(const SimpleLogger & log,
  163.                                              const vxistring & time,
  164.                                              VXIint & result)
  165. {
  166.   result = 0;
  167.   double final = 0.0, fraction = -1.0;
  168.   bool fflag = false;
  169.   
  170.   if (time.empty()) {
  171.     log.LogDiagnostic(0, L"PropertyList::ConvertTimeToMilliseconds - empty");
  172.     return false;
  173.   }
  174.   for (unsigned int i = 0; i < time.length(); ++i) {
  175.     char c;
  176.     switch (time[i]) {
  177.     // ignore white spaces and plus sign
  178.     case L' ': case L't': case L'n': case L'r':
  179.     case L'+':  continue;
  180.     case L'-': // do not allow negative conversion
  181.       log.StartDiagnostic(0) << L"PropertyList::ConvertTimeToMilliseconds - "
  182.           L"Illegal negative time designation number "" << time << L"".";
  183.       log.EndDiagnostic();
  184.       return false;      
  185.     case L'0':  c = 0;  break;
  186.     case L'1':  c = 1;  break;
  187.     case L'2':  c = 2;  break;
  188.     case L'3':  c = 3;  break;
  189.     case L'4':  c = 4;  break;
  190.     case L'5':  c = 5;  break;
  191.     case L'6':  c = 6;  break;
  192.     case L'7':  c = 7;  break;
  193.     case L'8':  c = 8;  break;
  194.     case L'9':  c = 9;  break;
  195.     case L'.': 
  196.       // disallow illegal fraction number
  197.       if( fflag ) {
  198.         log.StartDiagnostic(0) << L"PropertyList::ConvertTimeToMilliseconds - "
  199.           L"Invalid fraction number "" << time << L"".";
  200.         log.EndDiagnostic();
  201.         return false;
  202.       }
  203.       fflag = true;  
  204.       continue;      
  205.     default:
  206.       const vxistring units = time.substr(i, time.length() - i);
  207.       if (units == L"ms") {
  208.         result = (VXIint) final;
  209.         return true;
  210.       }
  211.       if (units == L"s") {
  212.         result = (VXIint) (final * 1000);
  213.         return true;
  214.       }
  215.       // Otherwise the unit type is not recognized.
  216.       log.StartDiagnostic(0) << L"PropertyList::ConvertTimeToMilliseconds - "
  217.         L"Invalid units in value "" << time << L"".";
  218.       log.EndDiagnostic();
  219.       return false;
  220.     }
  221.     if( fflag ) {
  222.       fraction = fraction < 0 ? 0.1 : fraction / 10;
  223.       final += (fraction * c );
  224.     } 
  225.     else
  226.       final = 10.0*final + c;
  227.   }
  228.   // No units were specified. Signal VXI to throw semantic error
  229.   log.StartDiagnostic(0) << L"PropertyList::ConvertTimeToMilliseconds - "
  230.       L"Missing unit in value "" << time << L"".";
  231.   log.EndDiagnostic();
  232.   return false;
  233. }
  234. bool PropertyList::ConvertValueToFraction(const SimpleLogger & log,
  235.                                           const vxistring & value,
  236.                                           VXIflt32& result)
  237. {
  238. #if defined(__GNUC__)
  239.   // The G++ 2.95/3.0 implementation of basic_stringstream is faulty.
  240.   VXIchar * temp;
  241.   result = VXIflt32(wcstod(value.c_str(), &temp));
  242.   if (*temp) {
  243.     result = -1.0;
  244.     return false;
  245.   }
  246. #else
  247.   std::basic_stringstream<VXIchar> attrStream(value);
  248.   attrStream >> result;
  249.   if (attrStream.bad()) {
  250.     result = -1.0;
  251.     return false;
  252.   }
  253. #endif
  254.   return true;
  255. }
  256. void PropertyList::GetFetchobjCacheAttrs(const VXMLElement & elem,
  257.                                          PropertyList::CacheAttrType type,
  258.                                          VXIMapHolder & fetchobj) const
  259. {
  260.   vxistring attr;
  261.   const VXIchar * propName;
  262.   // (1) Attribute: maxage
  263.   // (1.1) Get the attribute - first locally, then from the defaults.
  264.   elem.GetAttribute(ATTRIBUTE_MAXAGE, attr); 
  265.   if (attr.empty()) {
  266.     switch (type) {
  267.     case PropertyList::Audio:      propName = L"audiomaxage";     break;
  268.     case PropertyList::Document:   propName = L"documentmaxage";  break;
  269.     case PropertyList::Grammar:    propName = L"grammarmaxage";   break;
  270.     case PropertyList::Object:     propName = L"objectmaxage";    break;
  271.     case PropertyList::Script:     propName = L"scriptmaxage";    break;
  272.     case PropertyList::Data:       propName = L"datamaxage";      break;
  273.     }
  274.     attr = toString(GetProperty(propName));
  275.   }
  276.   // (1.2) Process the value.
  277.   if (!attr.empty()) {
  278.     VXIint value = -1;
  279. #if defined(__GNUC__)
  280.   // The G++ 2.95/3.0 implementation of basic_stringstream is faulty.
  281.     VXIchar * temp;
  282.     value = VXIint(wcstol(attr.c_str(), &temp, 10));
  283.     if (*temp) value = -1;
  284. #else
  285.     std::basic_stringstream<VXIchar> attrStream(attr);
  286.     attrStream >> value;
  287.     if (attrStream.bad()) value = -1;
  288. #endif
  289.     if (value < 0) {
  290.       log.StartDiagnostic(0) << L"PropertyList::GetFetchobjCacheAttrs - "
  291.         L"Invalid value for " << propName << L" of " << attr << L".";
  292.       log.EndDiagnostic();
  293.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
  294.                                            L"invalid maxage value");
  295.     }
  296.     // (1.3) Add value to fetchobj
  297.     VXIInteger * val = VXIIntegerCreate(value);
  298.     if (val == NULL) throw VXIException::OutOfMemory();
  299.     VXIvalueResult r = VXIMapSetProperty(fetchobj.GetValue(),
  300.                                          INET_CACHE_CONTROL_MAX_AGE,
  301.                                          reinterpret_cast<VXIValue*>(val));
  302.   }
  303.   // (2) Attribute: maxstale
  304.   // (2.1) Get the attribute - first locally, then from the defaults.
  305.   attr.erase();
  306.   elem.GetAttribute(ATTRIBUTE_MAXSTALE, attr);
  307.   if (attr.empty()) {
  308.     switch (type) {
  309.     case PropertyList::Audio:      propName = L"audiomaxstale";     break;
  310.     case PropertyList::Document:   propName = L"documentmaxstale";  break;
  311.     case PropertyList::Grammar:    propName = L"grammarmaxstale";   break;
  312.     case PropertyList::Object:     propName = L"objectmaxstale";    break;
  313.     case PropertyList::Script:     propName = L"scriptmaxstale";    break;
  314.     case PropertyList::Data:       propName = L"datamaxstale";      break;
  315.     }
  316.     attr = toString(GetProperty(propName));
  317.   }
  318.   // (2.2) Process the value.
  319.   if (!attr.empty()) {
  320.     VXIint value = -1;
  321. #if defined(__GNUC__)
  322.   // The G++ 2.95/3.0 implementation of basic_stringstream is faulty.
  323.     VXIchar * temp;
  324.     value = VXIint(wcstol(attr.c_str(), &temp, 10));
  325.     if (*temp) value = -1;
  326. #else
  327.     std::basic_stringstream<VXIchar> attrStream(attr);
  328.     attrStream >> value;
  329.     if (attrStream.bad()) value = -1;
  330. #endif
  331.     if (value < 0) {
  332.       log.StartDiagnostic(0) << L"PropertyList::GetFetchobjCacheAttrs - "
  333.         L"Invalid value for " << propName << L" of " << attr << L".";
  334.       log.EndDiagnostic();
  335.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
  336.                                            L"invalid maxstale value");
  337.     }
  338.     // (2.3) Add value to fetchobj
  339.     VXIInteger * val = VXIIntegerCreate(value);
  340.     if (val == NULL) throw VXIException::OutOfMemory();
  341.     VXIvalueResult r = VXIMapSetProperty(fetchobj.GetValue(),
  342.                                          INET_CACHE_CONTROL_MAX_STALE,
  343.                                          reinterpret_cast<VXIValue*>(val));
  344.   }
  345.   // (3) Attribute: fetchtimeout
  346.   // (3.1) Get the attribute - first locally, then from the defaults.
  347.   attr.erase();
  348.   elem.GetAttribute(ATTRIBUTE_FETCHTIMEOUT, attr);
  349.   if (attr.empty())
  350.     attr = toString(GetProperty(L"fetchtimeout"));
  351.   // (3.2) Process the value.
  352.   if (!attr.empty()) {
  353.     VXIint value;
  354.     if (!ConvertTimeToMilliseconds(log, attr, value)) {
  355.       log.StartDiagnostic(0) << L"PropertyList::GetFetchobjTimeout - "
  356.         L"Invalid value for fetchtimeout of " << attr << L".";
  357.       log.EndDiagnostic();
  358.       throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC,
  359.                                            L"invalid fetchtimeout value");
  360.     }
  361.     
  362.     // throw an bad.fetch if the fetchtimeout = 0
  363.     if( value == 0 )
  364.       throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH,
  365.                                            L"fetchtimeout value is 0s");  
  366.     
  367.     // (3.3) Add value to fetchobj
  368.     VXIInteger * str = VXIIntegerCreate(value);
  369.     if (str == NULL) throw VXIException::OutOfMemory();
  370.     VXIvalueResult r = VXIMapSetProperty(fetchobj.GetValue(),INET_TIMEOUT_DOWNLOAD,
  371.                                          reinterpret_cast<VXIValue*>(str));
  372.   }
  373.   // (4) Base uri from inet
  374.   attr.erase();
  375.   elem.GetAttribute(ATTRIBUTE_BASE, attr);
  376.   if (attr.empty()) {
  377.     const VXIchar* tmp = GetProperty(PropertyList::BaseURI);
  378.     if( tmp ) attr = tmp;
  379.   }
  380.   
  381.   if( !attr.empty() ) { 
  382.     VXIString * base = VXIStringCreate(attr.c_str());
  383.     if (base == NULL) throw VXIException::OutOfMemory();
  384.     VXIMapSetProperty(fetchobj.GetValue(), INET_URL_BASE,
  385.                     reinterpret_cast<VXIValue*>(base));
  386.   }
  387.   
  388. #pragma message ("PropertyList::GetFetchobjCacheAttrs - ignoring fetchhint")
  389. }
  390. bool PropertyList::GetFetchobjSubmitAttributes(const VXMLElement & elem,
  391.                                                VXIMapHolder & submitData,
  392.                                                VXIMapHolder & fetchobj) const
  393. {
  394.   // (1) Get encoding for the submit.
  395.   vxistring enctype;
  396.   bool hasenc;
  397.   // (1.1) Get the attribute.
  398.   hasenc = elem.GetAttribute(ATTRIBUTE_ENCTYPE, enctype);
  399.   if (!hasenc )
  400.     enctype = L"application/x-www-form-urlencoded";
  401.   // (1.2) Add value to fetchobj
  402.   VXIvalueResult r;
  403.   VXIString * str = VXIStringCreate(enctype.c_str());
  404.   if (str == NULL) throw VXIException::OutOfMemory();
  405.   r = VXIMapSetProperty(fetchobj.GetValue(), INET_SUBMIT_MIME_TYPE,
  406.                         reinterpret_cast<VXIValue*>(str));
  407.   if (r != VXIvalue_RESULT_SUCCESS) return false;
  408.   // (2) Set submit method.
  409.   // (2.1) Get the attribute - first locally, then from the defaults.
  410.   vxistring method;
  411.   elem.GetAttribute(ATTRIBUTE_METHOD, method);
  412.   // (2.2) Process the value.
  413.   const VXIchar * value = NULL;
  414.   if (method == L"get" || method.empty())
  415.   {
  416.     value = INET_SUBMIT_METHOD_GET;
  417.     if( hasenc )
  418.     {
  419.       log.StartDiagnostic(0) << L"PropertyList::GetFetchobjSubmitMethod - "
  420.       L"Ignoring Attribute 'enctype' for method 'GET'.";
  421.       log.EndDiagnostic();
  422.     }
  423.   }
  424.   else if (method == L"post")
  425.     value = INET_SUBMIT_METHOD_POST;
  426.   else {
  427.     log.StartDiagnostic(0) << L"PropertyList::GetFetchobjSubmitMethod - "
  428.       L"Bad value (" << ATTRIBUTE_METHOD << L" = "" << method << L""), "
  429.       L"defaulting to " << INET_SUBMIT_METHOD_DEFAULT << L".";
  430.     log.EndDiagnostic();
  431.     value = INET_SUBMIT_METHOD_DEFAULT;
  432.   }
  433.   // (2.3) Add value to fetchobj
  434.   str = VXIStringCreate(value);
  435.   if (str == NULL) throw VXIException::OutOfMemory();
  436.   r = VXIMapSetProperty(fetchobj.GetValue(), INET_SUBMIT_METHOD,
  437.                                        reinterpret_cast<VXIValue*>(str));
  438.   if (r != VXIvalue_RESULT_SUCCESS) return false;
  439.   // (3) Set the submit data.
  440.   r = VXIMapSetProperty(fetchobj.GetValue(), INET_URL_QUERY_ARGS,
  441.                         reinterpret_cast<VXIValue*>(submitData.Release()));
  442.   return r == VXIvalue_RESULT_SUCCESS;
  443. }
  444. bool PropertyList::GetFetchobjBase(VXIMapHolder & fetchobj) const
  445. {
  446.   const VXIchar * base = GetProperty(PropertyList::BaseURI);
  447.   if (base != NULL)
  448.     AddParamValue(fetchobj, INET_URL_BASE, base);
  449.   return true;
  450. }
  451. bool PropertyList::GetFetchobjURIs(const VXMLElement & elem,
  452.                                    VXIMapHolder & fetchobj,
  453.                                    vxistring & url,
  454.                                    vxistring & fragment) const
  455. {
  456.   fragment = L"";
  457.   // The URI may be decomposed into three elements - the base, a relative
  458.   // address, and a fragment corresponding to a dialog within that module.
  459.   // (1) First the base.
  460.   GetFetchobjBase(fetchobj);
  461.   // (2) Then determine if there is a fragment at all.
  462.   if (url.empty()) return false;
  463.   vxistring::size_type pos = url.find('#');
  464.   if (pos == vxistring::npos) return true;
  465.   // (3) There was a fragment.
  466.   if (pos + 1 < url.length())
  467.     fragment = url.substr(pos + 1, url.length() - pos - 1);
  468.   url.erase(pos);
  469.   return true;
  470. }
  471. //****************************************************************
  472. //* Recognition & Grammar property related.
  473. //****************************************************************
  474. bool PropertyList::PushProperties(const VXMLElement & doc)
  475. {
  476.   // (1) Find the last non-empty level.
  477.   unsigned int i;
  478.   for (i = LAST_PROP - 1; i > 0; --i)
  479.     if (!properties[i].empty()) break;
  480.   STRINGMAP & propmap = properties[ (i == LAST_PROP - 1 ? i : i + 1) ];  // This is the first empty level.
  481.   // (2) Add properties at that point.
  482.   bool foundSomething = false;
  483.   for (VXMLNodeIterator it(doc); it; ++it) {
  484.     VXMLNode child = *it;
  485.     if (child.GetType() != VXMLNode::Type_VXMLElement) continue;
  486.     const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(child);
  487.     VXMLElementType nodeName = elem.GetName();
  488.     if (nodeName == NODE_PROPERTY) {
  489.       vxistring name;
  490.       vxistring value;
  491.       elem.GetAttribute(ATTRIBUTE_NAME, name);
  492.       elem.GetAttribute(ATTRIBUTE_VALUE, value);
  493.       // This added the name / value pair, converting to vxistrings.
  494.       propmap.insert(propmap.end(), STRINGMAP::value_type(name, value));
  495.       foundSomething = true;
  496.     }
  497.   }
  498.   return foundSomething;
  499. }
  500. void PropertyList::PopProperties()
  501. {
  502.   // Find the last non-empty level.
  503.   unsigned int i;
  504.   for (i = LAST_PROP - 1; i > 0; --i)
  505.     if (!properties[i].empty()) break;
  506.   // Clear it.
  507.   properties[i].clear();
  508. }
  509. void PropertyList::PopPropertyLevel(PropertyLevel l)
  510. {
  511.   if( l < LAST_PROP && !properties[l].empty()) {
  512.     properties[l].clear();
  513.   }
  514. }
  515. // This starts at the lowest level, inserting entries into a newly created map.
  516. // Values at higher levels will overwrite keys with identical names.
  517. //
  518. void PropertyList::GetProperties(VXIMapHolder & m) const
  519. {
  520.   // (1) Collapse the values down into a single map.
  521.   STRINGMAP collapsed;
  522.   PROPERTIES::const_iterator i;
  523.   for (i = properties.begin(); i != properties.end(); ++i) {
  524.     for (STRINGMAP::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
  525.       collapsed[(*j).first] = (*j).second;
  526.     }
  527.   }
  528.   // (2) Create a new map & copy the values over.
  529.   STRINGMAP::const_iterator j;
  530.   for (j = collapsed.begin(); j != collapsed.end(); ++j) {
  531.     VXIString * value = VXIStringCreate((*j).second.c_str());
  532.     if (value == NULL) throw VXIException::OutOfMemory();
  533.     // Set this key.
  534.     VXIMapSetProperty(m.GetValue(), (*j).first.c_str(),
  535.                       reinterpret_cast<VXIValue *>(value));
  536.   }
  537. }