CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_4_4_5_patch3/src/FWCore/Modules/src/XMLOutputModule.cc

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 //
00003 // Package:     Modules
00004 // Class  :     XMLOutputModule
00005 //
00006 // Implementation:
00007 //     <Notes on implementation>
00008 //
00009 // Original Author:  Chris Jones
00010 //         Created:  Fri Aug  4 20:45:44 EDT 2006
00011 //
00012 
00013 // system include files
00014 #include <fstream>
00015 #include <string>
00016 #include <iomanip>
00017 #include <map>
00018 #include <sstream>
00019 #include <algorithm>
00020 
00021 #include "Reflex/Base.h"
00022 #include "Reflex/Member.h"
00023 
00024 #include "FWCore/Framework/interface/Event.h"
00025 #include "DataFormats/Provenance/interface/BranchDescription.h"
00026 #include "DataFormats/Provenance/interface/Selections.h"
00027 
00028 #include "FWCore/ParameterSet/interface/ParameterSet.h"
00029 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
00030 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
00031 
00032 #include "FWCore/Framework/interface/Frameworkfwd.h"
00033 #include "FWCore/Framework/interface/OutputModule.h"
00034 #include "FWCore/Framework/interface/GenericHandle.h"
00035 #include "FWCore/Framework/interface/MakerMacros.h"
00036 
00037 // user include files
00038 
00039 //
00040 // constants, enums and typedefs
00041 //
00042 
00043 namespace edm {
00044   class XMLOutputModule : public OutputModule {
00045 
00046    public:
00047       XMLOutputModule(ParameterSet const&);
00048       virtual ~XMLOutputModule();
00049       static void fillDescriptions(ConfigurationDescriptions& descriptions);
00050 
00051       // ---------- const member functions ---------------------
00052 
00053       // ---------- static member functions --------------------
00054 
00055       // ---------- member functions ---------------------------
00056 
00057    private:
00058       virtual void write(EventPrincipal const& e);
00059       virtual void writeLuminosityBlock(LuminosityBlockPrincipal const&) {}
00060       virtual void writeRun(RunPrincipal const&) {}
00061 
00062       XMLOutputModule(XMLOutputModule const&); // stop default
00063 
00064       XMLOutputModule const& operator=(XMLOutputModule const&); // stop default
00065 
00066       // ---------- member data --------------------------------
00067       std::ofstream stream_;
00068       std::string indentation_;
00069   };
00070 
00071   namespace {
00072     void doNotDelete(void*) {}
00073     void callDestruct(Reflex::Object* iObj) {
00074       iObj->Destruct();
00075     }
00076     //Handle memory for calls to Reflex Invoke
00077     // We handle Ref's by using an external void* buffer (which we do not delete) while everything else
00078     // we ask Reflex to create the proper object (and therefore must ask Reflex to delete it)
00079     boost::shared_ptr<Reflex::Object> initReturnValue(Reflex::Member const& iMember,
00080                                                       Reflex::Object* iObj,
00081                                                       void** iRefBuffer) {
00082       Reflex::Type returnType = iMember.TypeOf().ReturnType();
00083       if(returnType.IsReference()) {
00084         *iObj = Reflex::Object(returnType, iRefBuffer);
00085         return boost::shared_ptr<Reflex::Object>(iObj, doNotDelete);
00086       }
00087       *iObj = returnType.Construct();
00088       return boost::shared_ptr<Reflex::Object>(iObj, callDestruct);
00089     }
00090 
00091     //remove characters from a string which are not allowed to be used in XML
00092     std::string formatXML(std::string const& iO) {
00093       std::string result(iO);
00094       static std::string const kSubs("<>&");
00095       static std::string const kLeft("&lt;");
00096       static std::string const kRight("&gt;");
00097       static std::string const kAmp("&");
00098 
00099       std::string::size_type i = 0;
00100       while(std::string::npos != (i = result.find_first_of(kSubs, i))) {
00101         switch(result.at(i)) {
00102           case '<':
00103             result.replace(i, 1, kLeft);
00104             break;
00105           case '>':
00106             result.replace(i, 1, kRight);
00107             break;
00108           case '&':
00109             result.replace(i, 1, kAmp);
00110         }
00111         ++i;
00112       }
00113       return result;
00114     }
00115 
00116     char const* kNameValueSep = "\">";
00117     char const* kContainerOpen = "<container size=\"";
00118     char const* kContainerClose = "</container>";
00119     std::string const kObjectOpen = "<object type=\"";
00120     std::string const kObjectClose = "</object>";
00122     #define FILLNAME(_type_) s_toName[typeid(_type_).name()]= #_type_;
00123     std::string const& typeidToName(std::type_info const& iID) {
00124       static std::map<std::string, std::string> s_toName;
00125       if(s_toName.empty()) {
00126         FILLNAME(short);
00127         FILLNAME(int);
00128         FILLNAME(long);
00129         FILLNAME(long long);
00130 
00131         FILLNAME(unsigned short);
00132         FILLNAME(unsigned int);
00133         FILLNAME(unsigned long);
00134         FILLNAME(unsigned long long);
00135 
00136         FILLNAME(double);
00137         FILLNAME(float);
00138       }
00139       return s_toName[iID.name()];
00140     }
00141 
00142     template<typename T>
00143     void doPrint(std::ostream& oStream, std::string const& iPrefix, std::string const& iPostfix, Reflex::Object const& iObject, std::string const& iIndent) {
00144       oStream << iIndent << iPrefix << typeidToName(typeid(T)) << kNameValueSep
00145         << *reinterpret_cast<T*>(iObject.Address()) << iPostfix << "\n";
00146     }
00147 
00148     template<>
00149     void doPrint<char>(std::ostream& oStream, std::string const& iPrefix, std::string const& iPostfix, Reflex::Object const& iObject, std::string const& iIndent) {
00150       oStream << iIndent << iPrefix << "char" << kNameValueSep
00151         << static_cast<int>(*reinterpret_cast<char*>(iObject.Address())) << iPostfix << "\n";
00152     }
00153 
00154     template<>
00155     void doPrint<unsigned char>(std::ostream& oStream, std::string const& iPrefix, std::string const& iPostfix, Reflex::Object const& iObject, std::string const& iIndent) {
00156       oStream << iIndent << iPrefix << "unsigned char" << kNameValueSep << static_cast<unsigned int>(*reinterpret_cast<unsigned char*>(iObject.Address())) << iPostfix << "\n";
00157     }
00158 
00159     template<>
00160     void doPrint<bool>(std::ostream& oStream, std::string const& iPrefix, std::string const& iPostfix, Reflex::Object const& iObject, std::string const& iIndent) {
00161       oStream << iIndent << iPrefix << "bool" << kNameValueSep
00162         << ((*reinterpret_cast<bool*>(iObject.Address()))?"true":"false") << iPostfix << "\n";
00163     }
00164 
00165 
00166     typedef void(*FunctionType)(std::ostream&, std::string const&,
00167                                 std::string const&, Reflex::Object const&, std::string const&);
00168     typedef std::map<std::string, FunctionType> TypeToPrintMap;
00169 
00170     template<typename T>
00171     void addToMap(TypeToPrintMap& iMap){
00172       iMap[typeid(T).name()]=doPrint<T>;
00173     }
00174 
00175     bool printAsBuiltin(std::ostream& oStream,
00176                                std::string const& iPrefix,
00177                                std::string const& iPostfix,
00178                                Reflex::Object const iObject,
00179                                std::string const& iIndent){
00180       typedef void(*FunctionType)(std::ostream&, std::string const&, std::string const&, Reflex::Object const&, std::string const&);
00181       typedef std::map<std::string, FunctionType> TypeToPrintMap;
00182       static TypeToPrintMap s_map;
00183       static bool isFirst = true;
00184       if(isFirst){
00185         addToMap<bool>(s_map);
00186         addToMap<char>(s_map);
00187         addToMap<short>(s_map);
00188         addToMap<int>(s_map);
00189         addToMap<long>(s_map);
00190         addToMap<unsigned char>(s_map);
00191         addToMap<unsigned short>(s_map);
00192         addToMap<unsigned int>(s_map);
00193         addToMap<unsigned long>(s_map);
00194         addToMap<float>(s_map);
00195         addToMap<double>(s_map);
00196         isFirst=false;
00197       }
00198       TypeToPrintMap::iterator itFound =s_map.find(iObject.TypeOf().TypeInfo().name());
00199       if(itFound == s_map.end()){
00200 
00201         return false;
00202       }
00203       itFound->second(oStream, iPrefix, iPostfix, iObject, iIndent);
00204       return true;
00205     }
00206 
00207     bool printAsContainer(std::ostream& oStream,
00208                           std::string const& iPrefix,
00209                           std::string const& iPostfix,
00210                           Reflex::Object const& iObject,
00211                           std::string const& iIndent,
00212                           std::string const& iIndentDelta);
00213 
00214     void printDataMembers(std::ostream& oStream,
00215                           Reflex::Object const& iObject,
00216                           Reflex::Type const& iType,
00217                           std::string const& iIndent,
00218                           std::string const& iIndentDelta);
00219 
00220     void printObject(std::ostream& oStream,
00221                      std::string const& iPrefix,
00222                      std::string const& iPostfix,
00223                      Reflex::Object const& iObject,
00224                      std::string const& iIndent,
00225                      std::string const& iIndentDelta) {
00226       Reflex::Object objectToPrint = iObject;
00227       std::string indent(iIndent);
00228       if(iObject.TypeOf().IsPointer()) {
00229         oStream << iIndent << iPrefix << formatXML(iObject.TypeOf().Name(Reflex::SCOPED)) << "\">\n";
00230         indent +=iIndentDelta;
00231         int size = (0!=iObject.Address()) ? (0!=*reinterpret_cast<void**>(iObject.Address())?1:0) : 0;
00232         oStream << indent << kContainerOpen << size << "\">\n";
00233         if(size) {
00234           std::string indent2 = indent + iIndentDelta;
00235           Reflex::Object obj(iObject.TypeOf().ToType(), *reinterpret_cast<void**>(iObject.Address()));
00236           obj = obj.CastObject(obj.DynamicType());
00237           printObject(oStream, kObjectOpen, kObjectClose, obj, indent2, iIndentDelta);
00238         }
00239         oStream << indent << kContainerClose << "\n";
00240         oStream << iIndent << iPostfix << "\n";
00241         Reflex::Type pointedType = iObject.TypeOf().ToType();
00242         if(Reflex::Type::ByName("void") == pointedType || pointedType.IsPointer() || iObject.Address()==0) {
00243           return;
00244         }
00245         return;
00246 
00247         //have the code that follows print the contents of the data to which the pointer points
00248         objectToPrint = Reflex::Object(pointedType, iObject.Address());
00249         //try to convert it to its actual type (assuming the original type was a base class)
00250         objectToPrint = Reflex::Object(objectToPrint.CastObject(objectToPrint.DynamicType()));
00251         indent +=iIndentDelta;
00252       }
00253       std::string typeName(objectToPrint.TypeOf().Name(Reflex::SCOPED));
00254       if(typeName.empty()){
00255         typeName="{unknown}";
00256       }
00257 
00258       //see if we are dealing with a typedef
00259       Reflex::Type objectType = objectToPrint.TypeOf();
00260       bool wasTypedef = false;
00261       while(objectType.IsTypedef()) {
00262          objectType = objectType.ToType();
00263          wasTypedef = true;
00264       }
00265       if(wasTypedef){
00266          Reflex::Object tmp(objectType, objectToPrint.Address());
00267          objectToPrint = tmp;
00268       }
00269       if(printAsBuiltin(oStream, iPrefix, iPostfix, objectToPrint, indent)) {
00270         return;
00271       }
00272       if(printAsContainer(oStream, iPrefix, iPostfix, objectToPrint, indent, iIndentDelta)){
00273         return;
00274       }
00275 
00276       oStream << indent << iPrefix << formatXML(typeName) << "\">\n";
00277       printDataMembers(oStream, objectToPrint, objectType, indent+iIndentDelta, iIndentDelta);
00278       oStream << indent << iPostfix << "\n";
00279 
00280     }
00281 
00282     void printDataMembers(std::ostream& oStream,
00283                           Reflex::Object const& iObject,
00284                           Reflex::Type const& iType,
00285                           std::string const& iIndent,
00286                           std::string const& iIndentDelta) {
00287       //print all the base class data members
00288       for(Reflex::Base_Iterator itBase = iType.Base_Begin();
00289           itBase != iType.Base_End();
00290           ++itBase) {
00291         printDataMembers(oStream, iObject.CastObject(itBase->ToType()), itBase->ToType(), iIndent, iIndentDelta);
00292       }
00293       static std::string const kPrefix("<datamember name=\"");
00294       static std::string const ktype("\" type=\"");
00295       static std::string const kPostfix("</datamember>");
00296 
00297       for(Reflex::Member_Iterator itMember = iType.DataMember_Begin();
00298           itMember != iType.DataMember_End();
00299           ++itMember){
00300         //std::cout << "     debug " << itMember->Name() << " " << itMember->TypeOf().Name() << "\n";
00301         if (itMember->IsTransient()) {
00302           continue;
00303         }
00304         try {
00305           std::string prefix = kPrefix + itMember->Name() + ktype;
00306           printObject(oStream,
00307                       prefix,
00308                       kPostfix,
00309                       itMember->Get(iObject),
00310                       iIndent,
00311                       iIndentDelta);
00312         }catch(std::exception& iEx) {
00313           std::cout << iIndent << itMember->Name() << " <exception caught("
00314           << iEx.what() << ")>\n";
00315         }
00316       }
00317     }
00318 
00319     bool printContentsOfStdContainer(std::ostream& oStream,
00320                                      std::string const& iPrefix,
00321                                      std::string const& iPostfix,
00322                                      Reflex::Object iBegin,
00323                                      Reflex::Object const& iEnd,
00324                                      std::string const& iIndent,
00325                                      std::string const& iIndentDelta){
00326       size_t size = 0;
00327       std::ostringstream sStream;
00328       if(iBegin.TypeOf() != iEnd.TypeOf()) {
00329         std::cerr << " begin (" << iBegin.TypeOf().Name(Reflex::SCOPED) << ") and end ("
00330           << iEnd.TypeOf().Name(Reflex::SCOPED) << ") are not the same type" << std::endl;
00331         throw std::exception();
00332       }
00333       try {
00334         Reflex::Member compare(iBegin.TypeOf().MemberByName("operator!="));
00335         if(!compare) {
00336           //std::cerr << "no 'operator!=' for " << iBegin.TypeOf().Name() << std::endl;
00337           return false;
00338         }
00339         Reflex::Member incr(iBegin.TypeOf().MemberByName("operator++"));
00340         if(!incr) {
00341           //std::cerr << "no 'operator++' for " << iBegin.TypeOf().Name() << std::endl;
00342           return false;
00343         }
00344         Reflex::Member deref(iBegin.TypeOf().MemberByName("operator*"));
00345         if(!deref) {
00346           //std::cerr << "no 'operator*' for " << iBegin.TypeOf().Name() << std::endl;
00347           return false;
00348         }
00349 
00350         std::string indexIndent = iIndent+iIndentDelta;
00351         int dummy=0;
00352         //std::cerr << "going to loop using iterator " << iBegin.TypeOf().Name(Reflex::SCOPED) << std::endl;
00353 
00354         std::vector<void*> compareArgs = Reflex::Tools::MakeVector((iEnd.Address()));
00355         std::vector<void*> incrArgs = Reflex::Tools::MakeVector(static_cast<void*>(&dummy));
00356         bool compareResult;
00357         Reflex::Object objCompareResult(Reflex::Type::ByTypeInfo(typeid(bool)), &compareResult);
00358         Reflex::Object objIncr;
00359         void* objIncrRefBuffer;
00360         boost::shared_ptr<Reflex::Object> incrMemHolder = initReturnValue(incr, &objIncr, &objIncrRefBuffer);
00361         for(;
00362           compare.Invoke(iBegin, &objCompareResult, compareArgs), compareResult;
00363           incr.Invoke(iBegin, &objIncr, incrArgs), ++size) {
00364           //std::cerr << "going to print" << std::endl;
00365           Reflex::Object iTemp;
00366           void* derefRefBuffer;
00367           boost::shared_ptr<Reflex::Object> derefMemHolder = initReturnValue(deref, &iTemp, &derefRefBuffer);
00368           deref.Invoke(iBegin, &iTemp);
00369           if(iTemp.TypeOf().IsReference()) {
00370             iTemp = Reflex::Object(iTemp.TypeOf(), derefRefBuffer);
00371           }
00372           printObject(sStream, kObjectOpen, kObjectClose, iTemp, indexIndent, iIndentDelta);
00373           //std::cerr << "printed" << std::endl;
00374         }
00375       } catch(std::exception const& iE) {
00376         std::cerr << "while printing std container caught exception " << iE.what() << std::endl;
00377         return false;
00378       }
00379       oStream << iPrefix << iIndent << kContainerOpen << size << "\">\n";
00380       oStream << sStream.str();
00381       oStream << iIndent << kContainerClose << std::endl;
00382       oStream << iPostfix;
00383       //std::cerr << "finished loop" << std::endl;
00384       return true;
00385     }
00386 
00387     bool printAsContainer(std::ostream& oStream,
00388                           std::string const& iPrefix, std::string const& iPostfix,
00389                           Reflex::Object const& iObject,
00390                           std::string const& iIndent,
00391                           std::string const& iIndentDelta) {
00392       Reflex::Object sizeObj;
00393       try {
00394         size_t temp; //used to hold the memory for the return value
00395         sizeObj = Reflex::Object(Reflex::Type::ByTypeInfo(typeid(size_t)), &temp);
00396         iObject.Invoke("size", &sizeObj);
00397 
00398         if(sizeObj.TypeOf().TypeInfo() != typeid(size_t)) {
00399           throw std::exception();
00400         }
00401         size_t size = *reinterpret_cast<size_t*>(sizeObj.Address());
00402         Reflex::Member atMember;
00403         atMember = iObject.TypeOf().MemberByName("at");
00404         if(!atMember) {
00405           throw std::exception();
00406         }
00407         std::string typeName(iObject.TypeOf().Name(Reflex::SCOPED));
00408         if(typeName.empty()){
00409           typeName="{unknown}";
00410         }
00411 
00412         oStream << iIndent << iPrefix << formatXML(typeName) << "\">\n"
00413           << iIndent << kContainerOpen << size << "\">\n";
00414         Reflex::Object contained;
00415         std::string indexIndent=iIndent+iIndentDelta;
00416         for(size_t index = 0; index != size; ++index) {
00417           void* atRefBuffer;
00418           boost::shared_ptr<Reflex::Object> atMemHolder = initReturnValue(atMember, &contained, &atRefBuffer);
00419 
00420           atMember.Invoke(iObject, &contained, Reflex::Tools::MakeVector(static_cast<void*>(&index)));
00421           if(contained.TypeOf().IsReference()) {
00422             contained = Reflex::Object(contained.TypeOf(), atRefBuffer);
00423           }
00424           //std::cout << "invoked 'at'" << std::endl;
00425           try {
00426             printObject(oStream, kObjectOpen, kObjectClose, contained, indexIndent, iIndentDelta);
00427           }catch(std::exception& iEx) {
00428             std::cout << iIndent << " <exception caught("
00429             << iEx.what() << ")>\n";
00430           }
00431         }
00432         oStream << iIndent << kContainerClose << std::endl;
00433         oStream << iIndent << iPostfix << std::endl;
00434         return true;
00435       } catch(std::exception const& x){
00436         //std::cerr << "failed to invoke 'at' because " << x.what() << std::endl;
00437         try {
00438           //oStream << iIndent << iPrefix << formatXML(typeName) << "\">\n";
00439           std::string typeName(iObject.TypeOf().Name(Reflex::SCOPED));
00440           if(typeName.empty()){
00441             typeName="{unknown}";
00442           }
00443           Reflex::Object iObjBegin;
00444           void* beginRefBuffer;
00445           Reflex::Member beginMember = iObject.TypeOf().MemberByName("begin");
00446           boost::shared_ptr<Reflex::Object> beginMemHolder = initReturnValue(beginMember, &iObjBegin, &beginRefBuffer);
00447           Reflex::Object iObjEnd;
00448           void* endRefBuffer;
00449           Reflex::Member endMember = iObject.TypeOf().MemberByName("end");
00450           boost::shared_ptr<Reflex::Object> endMemHolder = initReturnValue(endMember, &iObjEnd, &endRefBuffer);
00451 
00452           beginMember.Invoke(iObject, &iObjBegin);
00453           endMember.Invoke(iObject, &iObjEnd);
00454           if(printContentsOfStdContainer(oStream,
00455                                          iIndent+iPrefix+formatXML(typeName)+"\">\n",
00456                                          iIndent+iPostfix,
00457                                          iObjBegin,
00458                                          iObjEnd,
00459                                          iIndent,
00460                                          iIndentDelta)) {
00461             if(typeName.empty()){
00462               typeName="{unknown}";
00463             }
00464             return true;
00465           }
00466         } catch(std::exception const& x) {
00467         }
00468         return false;
00469       }
00470       return false;
00471     }
00472 
00473     void printObject(std::ostream& oStream,
00474                      Event const& iEvent,
00475                      std::string const& iClassName,
00476                      std::string const& iModuleLabel,
00477                      std::string const& iInstanceLabel,
00478                      std::string const& iIndent,
00479                      std::string const& iIndentDelta) {
00480       try {
00481         GenericHandle handle(iClassName);
00482       }catch(edm::Exception const&) {
00483         std::cout << iIndent << " \"" << iClassName << "\"" << " is an unknown type" << std::endl;
00484         return;
00485       }
00486       GenericHandle handle(iClassName);
00487       iEvent.getByLabel(iModuleLabel, iInstanceLabel, handle);
00488       std::string className = formatXML(iClassName);
00489       printObject(oStream, kObjectOpen, kObjectClose, *handle, iIndent, iIndentDelta);
00490     }
00491   }
00492 
00493   //
00494   // static data member definitions
00495   //
00496 
00497   //
00498   // constructors and destructor
00499   //
00500   XMLOutputModule::XMLOutputModule(ParameterSet const& iPSet) :
00501       OutputModule(iPSet),
00502       stream_(iPSet.getUntrackedParameter<std::string>("fileName").c_str()),
00503       indentation_("  ") {
00504     if(!stream_){
00505       throw edm::Exception(errors::Configuration) << "failed to open file " << iPSet.getUntrackedParameter<std::string>("fileName");
00506     }
00507     stream_ << "<cmsdata>" << std::endl;
00508   }
00509 
00510   // XMLOutputModule::XMLOutputModule(XMLOutputModule const& rhs)
00511   // {
00512   //    // do actual copying here;
00513   // }
00514 
00515   XMLOutputModule::~XMLOutputModule() {
00516     stream_ << "</cmsdata>" << std::endl;
00517   }
00518 
00519   //
00520   // assignment operators
00521   //
00522   // XMLOutputModule const& XMLOutputModule::operator=(XMLOutputModule const& rhs)
00523   // {
00524   //   //An exception safe implementation is
00525   //   XMLOutputModule temp(rhs);
00526   //   swap(rhs);
00527   //
00528   //   return *this;
00529   // }
00530 
00531   //
00532   // member functions
00533   //
00534   void
00535   XMLOutputModule::write(EventPrincipal const& iEP) {
00536     ModuleDescription desc;
00537     Event event(const_cast<EventPrincipal&>(iEP), desc);
00538     stream_ << "<event run=\"" << event.id().run() << "\" number=\"" << event.id().event() << "\" >\n";
00539     std::string startIndent = indentation_;
00540     for(Selections::const_iterator itBD = keptProducts()[InEvent].begin(), itBDEnd = keptProducts()[InEvent].end();
00541         itBD != itBDEnd;
00542         ++itBD) {
00543       stream_ << "<product type=\"" << (*itBD)->friendlyClassName()
00544              << "\" module=\"" << (*itBD)->moduleLabel()
00545       << "\" productInstance=\"" << (*itBD)->productInstanceName() << "\">\n";
00546       printObject(stream_,
00547                    event,
00548                   (*itBD)->className(),
00549                   (*itBD)->moduleLabel(),
00550                   (*itBD)->productInstanceName(),
00551                   startIndent,
00552                   indentation_);
00553       stream_ << "</product>\n";
00554     }
00555     stream_ << "</event>" << std::endl;
00556   }
00557 
00558   void
00559   XMLOutputModule::fillDescriptions(ConfigurationDescriptions& descriptions) {
00560     ParameterSetDescription desc;
00561     desc.setComment("Prints event information into a file in XML format.");
00562     desc.addUntracked<std::string>("fileName");
00563     OutputModule::fillDescription(desc);
00564     descriptions.add("XMLoutput", desc);
00565   }
00566 }
00567 using edm::XMLOutputModule;
00568 DEFINE_FWK_MODULE(XMLOutputModule);