source: trunk/Mars/mfileio/MWriteFitsFile.cc@ 15715

Last change on this file since 15715 was 15277, checked in by lyard, 11 years ago
added fits files header key support
File size: 42.3 KB
Line 
1#include <TDataMember.h>
2#include <TClonesArray.h>
3
4#include "MArrayS.h"
5#include "MArrayB.h"
6
7#include "MRead.h"
8#include "MParList.h"
9#include "MStatusDisplay.h"
10
11#include "MLog.h"
12#include "MLogManip.h"
13#include "MWriteRootFile.h"
14
15#include "MWriteFitsFile.h"
16#include "MFitsArray.h"
17
18//for find
19#include <algorithm>
20
21ClassImp(MWriteFitsFile);
22
23using namespace std;
24
25map<MUniqueFileId, MTopFitsGroup> MWriteFitsFile::fTopFitsGroups;
26
27
28
29// ----------------------------------------------------------------------------
30//
31// if fileMode == kSingleFile
32// Opens the top level group file, if it was not already opened by an other
33// instance of MWriteFitsFile.
34// if fileMode == kMultiFiles
35// fname defines the rules to build the output filenames from the current
36// input filename. The files are opened in the ReInit() method.
37//
38// opt = RECREATE : an existing file will be deleted
39// = NEW : an already existing file will not be deleted and the new
40// file is not created. Check with IsFileOpen() if the new
41// top level group could be opened.
42// name will be the name of the group
43// title will be written as keyword TITLE into the header
44//
45MWriteFitsFile::MWriteFitsFile(const char *fname,
46 FILE_MODE fileMode,
47 const Option_t *opt,
48 const char *name,
49 const char *title)
50{
51 fOpenOption = opt;
52 if (name && name[0] != 0)
53 fGroupName = name;
54 else
55 fGroupName = "MARS";
56
57 if (title)
58 fTitle = title;
59
60 iTopFitsGroup = fTopFitsGroups.end();
61
62 if (fileMode == kMultiFiles)
63 fRule = fname;
64 else
65 *fLog << err << "The new version of MWriteFitsFile does not support multiple tables in one file. Please use kMultiFiles instead" << endl;
66 //ETIENNE DEPREC
67 // iTopFitsGroup will be set by OpenTopLevelGroup, if a top level
68 // group can be assigned to this MWriteFitsFile.
69 //OpenTopLevelGroup(fname);
70}
71
72MWriteFitsFile::MWriteFitsFile(const Int_t comp,
73 const char* rule,
74 const Option_t* option,
75 const char* ftitle,
76 const char* name,
77 const char* title)
78{
79 fOpenOption = option;
80 if (name && name[0] != 0)
81 fGroupName = name;
82 else
83 fGroupName = "MARS";
84 if (title) fTitle = ftitle;
85 iTopFitsGroup = fTopFitsGroups.end();
86 //always kMultiFiles
87 fRule = rule;
88}
89// ----------------------------------------------------------------------------
90//
91// Closes the top level group, it is is not used by an other instance of
92// MWriteFitsFile.
93// std::map<TString, std::ofits*>
94MWriteFitsFile::~MWriteFitsFile()
95{
96 for (std::map<TString, ofits*>::iterator it=fFitsTables.begin(); it!= fFitsTables.end();it++)
97 {
98 if (it->second != NULL)
99 {
100 it->second->close();
101 delete it->second;
102 }
103 }
104// CloseTopLevelGroup();
105 DeleteArrayHelper();
106}
107
108// ----------------------------------------------------------------------------
109//
110// If the same file is already used by an other instance of MWriteFitsFile,
111// the user counter is increasesd.
112// Else: If the file exist and option is "RECREATE", the file is deleted and
113// recreated.
114// Finally the file with an empty group file is created.
115//
116void MWriteFitsFile::OpenTopLevelGroup(const char * fname)
117{
118 // get rid of environment variables, ~, ... in filename
119 char * expandedFileName = gSystem->ExpandPathName(fname);
120
121 // get unique identifier of the new file
122 struct stat fileId;
123 if (stat(expandedFileName, &fileId) == 0)
124 {
125 // file exist already
126 iTopFitsGroup = fTopFitsGroups.find(MUniqueFileId(fileId.st_dev, fileId.st_ino));
127 if (iTopFitsGroup != fTopFitsGroups.end())
128 {
129 *fLog << err << "You are trying to use an already existing file. Please choose a different file name (" << fname << ")" << endl;
130 exit(0);
131 // this group is already open. we use it as well
132 *fLog << inf2 << "Found open file '" << GetFileName() << "'... re-using." << endl;
133 iTopFitsGroup->second.IncreaseUsage();
134 delete [] expandedFileName;
135 return;
136 }
137
138 // file exist, but we cannot use
139 if (fOpenOption == "RECREATE")
140 // remove the file
141 unlink(expandedFileName);
142
143 else if (fOpenOption == "NEW")
144 {
145 *fLog << err << "File '" << expandedFileName << "' exist already." << endl;
146 delete [] expandedFileName;
147 return;
148 }
149 else
150 {
151 *fLog << err << "Unsupported option (" << fOpenOption.Data() << ") to open file " <<
152 expandedFileName << endl;
153 delete [] expandedFileName;
154 return;
155 }
156 }
157
158 // file does not yet exist or was deleted
159
160// try {
161// AstroRootGroup topGroup;
162// char wrTitle[fTitle.Length() + 1];
163// if (fTitle.Length() > 0)
164// {
165// strcpy(wrTitle, fTitle.Data());
166// topGroup.InitAttr("TITLE", "A", wrTitle);
167// }
168
169 //ETIENNE do not open top level group as using one file per table
170 //I kept this code active anyway, because it is checked elsewhere in the code that iTopFitsGroup is set.
171 ofits* topGroup = NULL;
172// ofits* topGroup = new ofits();
173// TString dol = expandedFileName;
174// topGroup->open(dol.Data());
175
176
177 // store this new file in fTopFitsGroups and get the iterator to the
178 // new group
179 stat(expandedFileName, &fileId);
180 iTopFitsGroup = fTopFitsGroups.insert(pair<MUniqueFileId, MTopFitsGroup>
181 (MUniqueFileId(fileId.st_dev, fileId.st_ino),
182 MTopFitsGroup(expandedFileName, topGroup) )).first;
183
184 delete [] expandedFileName;
185
186}
187
188// --------------------------------------------------------------------------
189//
190// Reduces the use - counter of the top level group. Closes the group
191// table, if the counter is 0, i.e. no other MWriteFitsFile instance uses
192// the same top level group.
193//
194
195void MWriteFitsFile::CloseTopLevelGroup()
196{
197 if (iTopFitsGroup != fTopFitsGroups.end())
198 {
199 iTopFitsGroup->second.DecreaseUsage();
200 if (iTopFitsGroup->second.GetNumUsage() <= 0)
201 fTopFitsGroups.erase(iTopFitsGroup);
202
203 iTopFitsGroup = fTopFitsGroups.end();
204 }
205}
206
207// --------------------------------------------------------------------------
208//
209// returns the name of the top level group file. It may be expanded if the
210// name, given by the user, contained environment variables of the ~ sign.
211// If no top level group is open, "no_open_file" is returned.
212//
213const char * MWriteFitsFile::GetFileName() const
214{
215 if (IsFileOpen())
216 return iTopFitsGroup->second.GetFileName().Data();
217 else
218 return "no_open_file";
219}
220
221// --------------------------------------------------------------------------
222//
223// Add a new Container to list of containers which should be written to the
224// file. Give the name of the container which will identify the container
225// in the parameterlist. tname is the name of the FITS table to which the
226// container should be written (Remark: one table can hold more than one
227// container). The default is the same name as the container name.
228// If "mus"t is set to kTRUE (the default), then the container must exist
229// in the parameterlist. The container will be ignored, if "must" is set to
230// kFALSE and the container does not exist in the parameterlist.
231//
232void MWriteFitsFile::AddContainer(const char *cName,
233 const char *tName,
234 Bool_t must)
235{
236 if (cName == NULL || strlen(cName) == 0)
237 {
238 *fLog << warn << "Warning - container name in method MWriteFitsFile::"
239 "AddContainer not defined... ignored" << endl;
240 return;
241 }
242
243 // if tName is not defined, we use cName as the table name
244 TString tableName;
245 if (tName == NULL || strlen(tName) == 0)
246 tableName = cName;
247 else
248 tableName = tName;
249
250 // try to insert this container to the list of sub-tables. Write a
251 // warning, if the same container name was already defined and ignore
252 // this one.
253 if (fSubTables[tableName].insert( pair<TString, MFitsSubTable>
254 (cName, MFitsSubTable(must))).second == false)
255 {
256 *fLog << warn << "Warning - Container '"<< cName <<"' in table '" <<
257 tableName.Data() << "' already scheduled... ignored." << endl;
258 }
259}
260
261// --------------------------------------------------------------------------
262//
263// Add a new Container to list of containers which should be written to the
264// file. tname is the name of the FITS table to which the
265// container should be written (Remark: one table can hold more than one
266// container). The default is the same name as the container name.
267// "must" is ignored. It is just for compatibility with MWriteRootFiles.
268//
269void MWriteFitsFile::AddContainer(MParContainer *cont, const char *tName,
270 Bool_t must)
271{
272 if (cont == NULL)
273 {
274 *fLog << warn << "Warning - container in method MWriteFitsFile::"
275 "AddContainer not defined... ignored" << endl;
276 return;
277 }
278
279 TString tableName;
280 if (tName == NULL || strlen(tName) == 0)
281 tableName = cont->IsA()->GetName();
282 else
283 tableName = tName;
284
285 TString cName = cont->IsA()->GetName();
286 if (fSubTables[tableName].insert( pair<TString, MFitsSubTable>
287 (cName, MFitsSubTable(cont, must))).second == false)
288 {
289 *fLog << warn << "Warning - Container '"<< cName.Data() <<
290 "' in table '" << tableName.Data() <<
291 "' already scheduled... ignored." << endl;
292 }
293}
294
295// --------------------------------------------------------------------------
296//
297// Tries to open the given output file.
298//
299Int_t MWriteFitsFile::PreProcess(MParList *pList)
300{
301 if (fRule.Length() != 0)
302 // the file is not yet open
303 return kTRUE;
304 //
305 // test whether file is now open or not
306 //
307 if (!IsFileOpen())
308 {
309 *fLog << err << dbginf << "Cannot open file '" << GetFileName() << "'" << endl;
310 return kFALSE;
311 }
312
313 *fLog << inf << "File '" << GetFileName() << "' open for writing." << endl;
314
315 //
316 // Get the containers (pointers) from the parameter list you want to write
317 //
318 if (!GetContainer(pList))
319 return kFALSE;
320
321 //
322 // write the container if it is already in changed state
323 //
324 return CheckAndWrite();
325
326}
327
328Int_t MWriteFitsFile::PostProcess()
329{
330 for (std::map<TString, ofits*>::iterator it=fFitsTables.begin(); it!= fFitsTables.end();it++)
331 {
332 it->second->close();
333 delete it->second;
334 it->second = NULL;
335 }
336
337 return kTRUE;
338}
339template<>
340std::string MWriteFitsFile::GetFitsString(const double& value)
341{
342 std::ostringstream returnVal;
343 returnVal << std::setprecision(value>1e-100 && value<1e100 ? 15 : 14) << value;
344 std::string temp = returnVal.str();
345 std::replace(temp.begin(), temp.end(), 'e', 'E');
346 if (temp.find_first_of('E')==std::string::npos && temp.find_first_of('.')==std::string::npos)
347 temp += ".";
348 return temp;
349}
350template<>
351std::string MWriteFitsFile::GetFitsString(const float& value)
352{
353 return GetFitsString((double)(value));
354}
355// --------------------------------------------------------------------------
356//
357// Opens all FITS files for all container, which were registered with the
358// AddContainer() - methods. The container is taken from the pList, if it
359// was registered just by its name.
360// Calling the AddContainer() after calling this method will not add new
361// containers to the list.
362//
363Bool_t MWriteFitsFile::GetContainer(MParList *pList)
364{
365 if (iTopFitsGroup == fTopFitsGroups.end())
366 // something went wrong while the top level group was created
367 return kFALSE;
368
369
370 // remove the extension from the filename
371 // this has been disabled for now
372 char fileNameNoExt[strlen(GetFileName()) + 1];
373 strcpy(fileNameNoExt, GetFileName());
374// char * pos = strrchr(fileNameNoExt, '.');
375// if (pos) *pos = 0;
376//*fLog << inf <<"Filename no ext: " << fileNameNoExt << endl;
377 // loop over all FITS tables which have to be created.
378 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
379 fSubTables.begin();
380 while (i_table != fSubTables.end())
381 {
382
383 ofits* fitsTable = new ofits();
384 fitsTable->AllowCommentsTrimming(true);
385 TString dol = fileNameNoExt;
386 //get rid of the ".root" extension in the file name (if any)
387 if (dol(dol.Length()-5, dol.Length()) == ".root")
388 {
389 dol = dol(0, dol.Length()-5);
390 }
391 if (dol(dol.Length()-5, dol.Length()) == ".fits")
392 {
393 dol = dol(0, dol.Length()-5);
394 }
395 dol += "_";
396 dol += i_table->first;
397 dol += ".fits";
398 fitsTable->open(dol.Data());
399 *fLog << inf << "Opening FITS file: " << dol.Data() << endl;
400 fFitsTables[i_table->first] = fitsTable;
401 fTableObjectCreated[i_table->first] = true;
402 fTableHeaderWritten[i_table->first] = false;
403
404 // loop over all containers, which define a sub-table of the current table
405 Int_t num = 0;
406 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
407 while (i_subTable != i_table->second.end())
408 {
409
410 MFitsSubTable & subTable = i_subTable->second;
411 if (subTable.GetContainer() == NULL)
412 {
413 // container address is not yet known
414 const char * cname = i_subTable->first.Data();
415 MParContainer *cont = (MParContainer*)pList->FindObject(cname);
416 if (!cont)
417 {
418 // no corresponding container is available in pList
419 if (subTable.MustHave())
420 {
421 *fLog << err << "Cannot find parameter container '" << cname << "'." << endl;
422 return kFALSE;
423 }
424
425 // we can ignore this container, delete it from the map
426 *fLog << inf2 << "Unnecessary parameter container '" << cname << "' not found..." << endl;
427 map<TString, MFitsSubTable>::iterator i_tmp = i_subTable;
428 i_subTable++;
429 i_table->second.erase(i_tmp);
430 continue;
431 }
432
433 // we have our container address and can use it
434 subTable.SetContainer(cont);
435 }
436
437 // initialize all columns of the sub-table, defined by the current container
438 TClass * cl = i_subTable->second.GetContainer()->IsA();
439 if (!InitColumns(i_table->first, TString(cl->GetName()) + ".", fitsTable,
440 i_subTable->second.GetContainer(), cl) )
441 return kFALSE;
442
443 char attrName[10];
444 sprintf(attrName, "CLNAME%d", num);
445 num++;
446
447 InitAttr(attrName,
448 "A",
449 (void*)i_subTable->first.Data(),
450 NULL,
451 "MARS container name",
452 fitsTable);
453
454 i_subTable++;
455 }
456
457 // in case not all sub-tables were removed, we can now create the FITS table
458 if (i_table->second.size() > 0)
459 {
460 // create the DOL of the table. It will be something like:
461 // fileNameNoExt_dataName.fits[dataName]
462 TString dol = fileNameNoExt;
463 dol += "_";
464 dol += i_table->first;
465 dol += ".fits";
466
467 if (fOpenOption == "RECREATE")
468 // remove the file
469 unlink(dol.Data());
470
471 dol += "[";
472 dol += i_table->first;
473 dol += "]";
474// *fLog << err << "Reiner would have opened fits file: " << dol.Data() << endl;
475 //exit(0);
476 //fitsTable->open(dol.Data());
477
478 // attach the new table to the top level group
479 //TODO make sure that this attach below is not needed...
480 //iTopFitsGroup->second.Attach(fitsTable);
481 }
482
483 i_table++;
484 }
485
486 return kTRUE;
487}
488void MWriteFitsFile::InitAttr(const char* attrName,
489 const char* dataType,
490 void* var,
491 const char* unit,
492 const char* comment,
493 ofits* outFile)
494{
495 if (outFile == NULL)
496 return;
497 string dts(dataType);
498 string ans(attrName);
499 string cs;
500 if (comment != NULL)
501 cs = string(comment);
502 else
503 cs = "";
504 ostringstream val;
505 if ((dts == "bool") || (dts == "Bool_t") || (dts == "L"))
506 {
507 outFile->SetBool(ans, ((bool*)(var))[0], cs);
508 return;
509 }
510 if ((dts == "char") || (dts == "Char_t") || (dts == "S"))
511 {
512 val << ((char*)(var))[0];
513 outFile->SetStr(ans, val.str(), cs);
514 return;
515 }
516 if ((dts == "unsigned char") || (dts == "UChar_t") || (dts == "B"))
517 {
518 val << ((unsigned char*)(var))[0];
519 outFile->SetStr(ans, val.str(), cs);
520 return;
521 }
522 if ((dts == "short") || (dts == "Short_t") || (dts == "I"))
523 {
524 val << ((short*)(var))[0];
525 outFile->SetStr(ans, val.str(), cs);
526 return;
527 }
528 if ((dts == "unsigned short") || (dts == "UShort_t") || (dts == "U"))
529 {
530 val << ((unsigned short*)(var))[0];
531 outFile->SetStr(ans, val.str(), cs);
532 return;
533 }
534 if ((dts == "int") || (dts == "Int_t") || (dts == "V"))
535 {
536 outFile->SetInt(ans, ((int*)(var))[0], cs);
537 return;
538 }
539 if ((dts == "unsigned int") || (dts == "UInt_t") || (dts == "V"))
540 {
541 outFile->SetInt(ans, ((unsigned int*)(var))[0], cs);
542 return;
543 }
544 if ((dts == "long long") || (dts == "Long64_t") || (dts == "K"))
545 {
546 outFile->SetInt(ans, ((long long*)(var))[0], cs);
547 return;
548 }
549 if ((dts == "unsigned long long") || (dts == "ULong64_t") || (dts == "W"))
550 {
551 val << ((unsigned long long*)(var))[0];
552 outFile->SetStr(ans, val.str(), cs);
553 return;
554 }
555 if ((dts == "float") || (dts == "TFloat_t") || (dts == "E"))
556 {
557 outFile->SetFloat(ans, ((float*)(var))[0], cs);
558 return;
559 }
560 if ((dts == "double") || (dts == "TDouble_t") || (dts == "D"))
561 {
562 outFile->SetFloat(ans, ((double*)(var))[0], cs);
563 return;
564 }
565 if ((dts == "char*") || (dts == "A"))
566 {
567 outFile->SetStr(ans, string((char*)(var)), cs);
568 return;
569 }
570 //trigger error
571 *fLog << err << "Format string not recognized while adding header entry " << ans << " with type " << dts << endl;
572}
573// --------------------------------------------------------------------------
574//
575// This method is called when new data should be written to the FITS table.
576// In general only one row is written. (There are exceptions for Arrays).
577// A new row to a FITS table is written if at least for one container
578// (one sub-table) the ReadyToSave - flag is set.
579//
580Bool_t MWriteFitsFile::CheckAndWrite()
581{
582// try {
583
584 // loop over all tables
585 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
586 fSubTables.begin();
587 while (i_table != fSubTables.end())
588 {
589 // is this table open?
590 if (fFitsTables.find(i_table->first) != fFitsTables.end())
591 {
592
593 // loop over all sub-tables
594 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
595 while (i_subTable != i_table->second.end())
596 {
597
598 if (i_subTable->second.GetContainer()->IsReadyToSave())
599 {
600 // first write the TClonesArray and set the size of the arrays
601 list<MArrayHelperBase*> & clHelper = fClHelper[i_table->first];
602 list<MArrayHelperBase*>::iterator i_clHelper = clHelper.begin();
603 while (i_clHelper != clHelper.end())
604 {
605 // write all entries in the TClonesArray in its FITS table
606 //removed this write because I do it elsewhere. is that alright ?
607 // (*i_clHelper)->Write();
608 i_clHelper++;
609 }
610
611 // write one line to this table
612 writeOneRow(i_table->first);
613// fFitsTables[i_table->first].Write();
614 break;
615 }
616 i_subTable++;
617 }
618 }
619
620 i_table++;
621 }
622// }
623// catch (exception &e)
624 // {
625 // *fLog << err << e.what() << endl;
626 // return kFALSE;
627 // }
628
629 return kTRUE;
630}
631
632string MWriteFitsFile::Trim(const string &str)
633{
634 // Trim Both leading and trailing spaces
635 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
636 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
637
638 // if all spaces or empty return an empty string
639 if (string::npos==first || string::npos==last)
640 return string();
641
642 return str.substr(first, last-first+1);
643}
644
645Bool_t MWriteFitsFile::VetoColumn(const std::string& colName)
646{
647 for (std::vector<string>::iterator it=fVetoedColumns.begin(); it != fVetoedColumns.end(); it++)
648 if (*it == colName)
649 {
650 *fLog << warn << "Warning: column " << colName << " is being vetoed twice" << endl;
651 return kFALSE;
652 }
653 fVetoedColumns.push_back(colName);
654 return kTRUE;
655}
656
657Bool_t MWriteFitsFile::SetBytesPerSample(const std::string& colName, uint32_t numBytes)
658{
659 for (map<string, uint32_t>::iterator it=fBytesPerSamples.begin(); it!=fBytesPerSamples.end(); it++)
660 if (it->first == colName)
661 {
662 *fLog << warn << "Warning: column " << colName << " bytes per sample is being redefined twice" << endl;
663 return kFALSE;
664 }
665 if (numBytes != 1 && numBytes != 2 && numBytes != 4 && numBytes != 8)
666 {
667 *fLog << warn << "Only powers of two are allowed for types re-mapping." << endl;
668 return kFALSE;
669 }
670 fBytesPerSamples[colName] = numBytes;
671 return kTRUE;
672}
673
674// --------------------------------------------------------------------------
675//
676// Initialize all columns in "fitsTable" of the class "classDef". The data
677// of this class are stored in a buffer, beginning at "baseAdr".
678//
679Bool_t MWriteFitsFile::InitColumns(const TString & tableName,
680 const TString & parentVarName,
681 ofits* fitsTable,
682 void * baseAdr,
683 TClass * classDef)
684{
685 // get all data members of the class
686 TList * dataMembers = classDef->GetListOfDataMembers();
687 TIter next(dataMembers);
688 TDataMember * dataMember;
689
690 // loop over all data members
691 while ((dataMember = (TDataMember*)next()) != NULL)
692 {
693 if (!dataMember->IsPersistent())
694 // don't store this variable
695 continue;
696
697 if (dataMember->Property() & ( G__BIT_ISENUM | G__BIT_ISSTATIC))
698 // we cannot store this
699 continue;
700
701 if (strcmp(dataMember->GetTrueTypeName(), "TClass*") == 0)
702 // we don't want to store this.
703 continue;
704
705 // is it an array of more than 1 dimension?
706 if (dataMember->GetArrayDim() > 1)
707 {
708 *fLog << err << "Two and more dimensional arrays of member variables"
709 " are not supported." << endl;
710 *fLog << "See variable " << dataMember->GetName() <<
711 " in container " << classDef->GetName() << endl;
712 return kFALSE;
713 }
714
715
716 // replace � by **2 in the comment field
717 string comment(dataMember->GetTitle());
718 string::size_type pos1, pos2;
719 if ((pos1 = comment.find(178)) != string::npos)
720 comment.replace(pos1, 1, "**2");
721
722 // get the optional mapping to the fits column names
723 string fitsOptions="";
724 string unit="unit";
725
726 pos1 = comment.find("{fits: ");
727 pos2 = comment.find('}');
728 std::map<string, string> fitsTokens;
729 if (pos1 != string::npos && pos2 != string::npos)
730 {
731 fitsOptions=comment.substr(pos1+7, (pos2-pos1)-7);
732 //do we have more than one option ?
733 string::size_type pos3= fitsOptions.find_first_of(';');
734 string::size_type pos4 = string::npos;
735 string key="";
736 string value="";
737 string keyValue="";
738 while (pos3 != string::npos)
739 {//we have at least 2 options left
740 // *fLog << err << "fitsOptions: " << fitsOptions << endl;
741 keyValue = fitsOptions.substr(0,pos3);
742 // *fLog << err << "keyValue: " << keyValue << endl;
743 pos4 = keyValue.find('=');
744 if (pos4 == string::npos)
745 {
746 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
747 return kFALSE;
748 }
749 key = Trim(keyValue.substr(0, pos4));
750 value = Trim(keyValue.substr(pos4+1, pos3));
751 fitsTokens[key] = value;
752 // *fLog << err << "key: " << key << " value: " << value << endl;
753 fitsOptions = fitsOptions.substr(pos3+1, fitsOptions.size());
754 pos3 = fitsOptions.find_first_of(';');
755 }
756// *fLog << err << "fitsOptions: " << fitsOptions << endl;
757 keyValue = fitsOptions;
758 pos4 = keyValue.find('=');
759 if (pos4 == string::npos)
760 {
761 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
762 return kFALSE;
763 }
764 key = Trim(keyValue.substr(0, pos4));
765 value = Trim(keyValue.substr(pos4+1, pos3));
766 fitsTokens[key] = value;
767// *fLog << err << "key: " << key << " value: " << value << endl;
768 }
769
770 TString colName = parentVarName + dataMember->GetName();
771
772 if (fitsTokens.find("unit") != fitsTokens.end())
773 unit = fitsTokens["unit"];
774 if (fitsTokens.find("name") != fitsTokens.end())
775 colName = fitsTokens["name"];
776
777 //check for column veto
778 if (std::find(fVetoedColumns.begin(), fVetoedColumns.end(), colName.Data())!=fVetoedColumns.end())
779 {
780 *fLog << inf << "Vetoing column " << colName.Data() << endl;
781 continue;
782 }
783
784 // set the array size
785 TString dataType = dataMember->GetTrueTypeName();
786
787 uint32_t dataLength = 0;
788 switch (dataMember->GetArrayDim())
789 {
790 case 0:
791 dataLength = 1;
792 break;
793 case 1:
794 dataLength = dataMember->GetMaxIndex(0);
795 break;
796 default:
797 *fLog << err << "n-dimensional array should have been discarded already " << colName.Data();
798 break;
799 };
800// if (dataLength == 0)
801// continue;
802
803 if (dataMember->Property() & G__BIT_ISCLASS)
804 {
805 // special treatment for classes
806 uint32_t arraySize = 0;
807 TString typeName;
808
809 if (strncmp(dataMember->GetTrueTypeName(), "MArray", 6) == 0)
810 {
811// MArrayHelperBase * clHelper;
812 if (strcmp(dataMember->GetTrueTypeName(), "MArrayS*") == 0)
813 {
814 typeName = "UShort_t";
815 arraySize = (*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
816 InitSingleColumn(tableName,
817 arraySize,
818 typeName.Data(),
819 (char*)(*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
820 colName.Data(),
821 unit,
822 comment);
823 }
824 else
825 {
826 if (strcmp(dataMember->GetTrueTypeName(), "MArrayB*") == 0)
827 {
828 typeName = "UChar_t";
829 arraySize = (*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
830 InitSingleColumn(tableName,
831 arraySize,
832 typeName.Data(),
833 (char*)(*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
834 colName.Data(),
835 unit,
836 comment);
837 }
838 else
839 {
840 *fLog << err << dataMember->GetTrueTypeName() << " not yet implemented." << endl;
841 return kFALSE;
842 }
843 }
844
845 continue;
846 }
847 else if (strcmp(dataMember->GetTrueTypeName(), "TClonesArray") == 0)
848 {
849 *fLog << warn << "I'm skipping the TClonesArray for now" << endl;
850 continue;
851 // each TClonesArray requires a FITS table by itself.
852 MClonesArrayHelper * clHelper;
853
854 TClonesArray * cloneArray = (TClonesArray*)((char*)baseAdr + dataMember->GetOffset());
855 Bool_t status;
856 clHelper = new MClonesArrayHelper(cloneArray, fLog, status);
857 if (!status) return status;
858
859 fClHelper[tableName].push_back(clHelper);
860
861 // add one column in the parent table of the TClonesArray to store the
862 // number of entries in the TClonesArray.
863 InitSingleColumn(tableName,
864 1,
865 "UInt_t",
866 clHelper->GetArraySizePtr(),
867 colName.Data(),
868 unit,
869 comment);
870
871 // initialize the columns of the new FITS table, which will store the
872 // data entries of the TClonesArray
873 if (InitColumns(TString("noName"),
874 colName + "_",
875 fitsTable,
876 clHelper->GetDataBuffer(),
877 cloneArray->GetClass())
878 == kFALSE)
879 return kFALSE;
880
881 // the columns are initialized. We can create the FITS table
882 if (clHelper->OpenFitsTable(GetFileName(), dataMember->GetName(),
883 fOpenOption, fLog) == kFALSE)
884 return kFALSE;
885 }
886
887 else
888 {
889 // the current container has a variable of an other class. We create
890 // also columns of this other class in the same table
891 TClass * newClassDef = TClass::GetClass(dataMember->GetTrueTypeName(), kFALSE, kTRUE);
892 if (newClassDef)
893 {
894 if (InitColumns(tableName, colName + ".", fitsTable, (char*)baseAdr + dataMember->GetOffset(),
895 newClassDef) == kFALSE)
896 return kFALSE;
897 }
898 else
899 *fLog << warn << "Cannot write data of class " << colName + "." + dataMember->GetTrueTypeName() << endl;
900 }
901 continue;
902 }
903
904 InitSingleColumn(tableName,
905 dataLength,
906 dataType.Data(),
907 (char*)baseAdr + dataMember->GetOffset(),
908 colName.Data(),
909 unit,
910 comment);
911
912 }
913 return kTRUE;
914}
915void MWriteFitsFile::InitSingleColumn(const TString& tableName,
916 uint32_t count,
917 const string& typeName,
918 void* dataPointer,
919 const string& columnName,
920 const string& unit,
921 const string& comment)
922{
923 if (!fTableObjectCreated[tableName])
924 {
925 *fLog << err << "ERROR: Cannot init column " << columnName << " before assigning object: " << tableName << endl;
926 return;
927 }
928 int typeFound = 0;
929 char typeChar = '0';
930 if ((typeName == "bool") || (typeName == "Bool_t") || (typeName == "L"))
931 {
932 typeChar = 'L';
933 typeFound++;
934 }
935 if ((typeName == "char") || (typeName == "Char_t") || (typeName == "S"))
936 {
937 typeChar = 'A';
938 typeFound++;
939 }
940 if ((typeName == "unsigned char") || (typeName == "UChar_t") || (typeName == "B"))
941 {
942 typeChar = 'A';
943// *fLog << warn << "Converting unsigned char to char in fits file" << endl;
944 typeFound++;
945 }
946 if ((typeName == "short") || (typeName == "Short_t") || (typeName == "I"))
947 {
948 typeChar = 'I';
949 typeFound++;
950 }
951 if ((typeName == "unsigned short") || (typeName == "UShort_t") || (typeName == "U"))
952 {
953 typeChar = 'I';
954// *fLog << warn << "Converting unsigned short to short in fits file" << endl;
955 typeFound++;
956 }
957 if ((typeName == "int") || (typeName == "Int_t") || (typeName == "V"))
958 {
959 typeChar = 'J';
960 typeFound++;
961 }
962 if ((typeName == "unsigned int") || (typeName == "UInt_t") || (typeName == "V"))
963 {
964 typeChar = 'J';
965// *fLog << warn << "Converting unsigned int to int in fits file" << endl;
966 typeFound++;
967 }
968 if ((typeName == "long long") || (typeName == "Long64_t") || (typeName == "K"))
969 {
970 typeChar = 'K';
971 typeFound++;
972 }
973 if ((typeName == "unsigned long long") || (typeName == "ULong64_t") || (typeName == "W"))
974 {
975 typeChar = 'K';
976// *fLog << warn << "Converting unsigned long to long in fits file" << endl;
977 typeFound++;
978 }
979 if ((typeName == "float") || (typeName=="TFloat_t") || (typeName == "E"))
980 {
981 typeChar = 'E';
982 typeFound++;
983 }
984 if ((typeName == "double") || (typeName == "TDouble_t") || (typeName == "D"))
985 {
986 typeChar = 'D';
987 typeFound++;
988 }
989// if ((typeName == "char*") || (typeName == "A"))
990// {
991// typeFound++;
992// }
993 if (typeFound != 1)
994 {
995 *fLog << err << "We have a problem with the data type: " << typeName << endl;
996 return;
997 }
998 uint32_t colWidth = 0;
999 switch (typeChar)
1000 {
1001 case 'L': colWidth = 1*count; break;
1002 case 'A': colWidth = 1*count; break;
1003 case 'B': colWidth = 1*count; break;
1004 case 'I': colWidth = 2*count; break;
1005 case 'J': colWidth = 4*count; break;
1006 case 'K': colWidth = 8*count; break;
1007 case 'E': colWidth = 4*count; break;
1008 case 'D': colWidth = 8*count; break;
1009 default:
1010 *fLog << err << "ERROR: typeChar could not be resolved to an actual type" << endl;
1011 };
1012 //check for type remapping here
1013 if (fBytesPerSamples.find(columnName) != fBytesPerSamples.end())
1014 {
1015 if (typeChar != 'A')
1016 {
1017 *fLog << err << "Attempt to remap type " << typeChar << " to " << fBytesPerSamples[columnName] << " is only allowed on bytes (variable name: " << columnName << "). Ignoring column" << endl;
1018 return;
1019 }
1020 uint32_t bytesPerSample = fBytesPerSamples.find(columnName)->second;
1021 if (colWidth%bytesPerSample != 0)
1022 {
1023 *fLog << err << "Type remapping cannot be done using " << bytesPerSample << " bytes per sample on an array of char of size " << colWidth << ". Ignoring column " << columnName << endl;
1024 return;
1025 }
1026 switch (bytesPerSample)
1027 {
1028 case 1: count = count/1; typeChar = 'A'; break;
1029 case 2: count = count/2; typeChar = 'I'; break;
1030 case 4: count = count/4; typeChar = 'J'; break;
1031 case 8: count = count/8; typeChar = 'K'; break;
1032 default:
1033 *fLog << err << "ERROR: num bytes per sample = " << bytesPerSample << " should have been forbidden already" << endl;
1034 }
1035
1036 }
1037
1038 fDataPointers[tableName].push_back(dataPointer);
1039 fTypeChars[tableName].push_back(typeChar);
1040 fColSizes[tableName].push_back(count);
1041 fColWidth[tableName].push_back(colWidth);
1042
1043 //FIXME ofits does not allow for much liberty regarding the size of the column names.
1044 //Truncating them badly here, will probably cause other problems -> Modify ofits.h instead
1045 string truncatedName=columnName.substr((columnName.size()>40)?columnName.size()-40:0,columnName.size());
1046 string truncatedComment = comment.substr((comment.size()>10)?comment.size()-10:0,comment.size());
1047// *fLog << warn << "In table " << tableName << " Adding column |" << truncatedName << "| |" << truncatedComment << "| |" << count << "| |" << typeChar;
1048// *fLog << warn << "| Real: "<< columnName << " comment: " << comment << endl;
1049 fFitsTables[tableName]->AddColumn(count, typeChar, truncatedName, unit, truncatedComment);
1050}
1051
1052void MWriteFitsFile::writeOneRow(const TString& tableName)
1053{
1054 if (!fTableHeaderWritten[tableName])
1055 {
1056 for (vector<ofits::Key>::const_iterator it = fHeaderKeys.begin(); it != fHeaderKeys.end(); it++)
1057 fFitsTables[tableName]->SetRaw(it->key, it->value, it->comment);
1058 fFitsTables[tableName]->WriteTableHeader(tableName.Data());
1059 fTableHeaderWritten[tableName] = true;
1060 }
1061 if (!fTableObjectCreated[tableName])
1062 {
1063 *fLog << err << "This is not good. Please initialize the fits table before writing to it: " << tableName << endl;
1064 return;
1065 }
1066 //first calculate the size of one row
1067 uint32_t rowWidth = 0;
1068 for (uint32_t i=0;i<fTypeChars[tableName].size();i++)
1069 rowWidth += fColWidth[tableName][i];
1070 unsigned char* tempBuffer = new unsigned char[rowWidth];
1071 //then copy the data to be written contiguously
1072 uint32_t bytesCounter = 0;
1073 for (uint32_t i=0;i<fDataPointers[tableName].size();i++)
1074 {
1075 memcpy(&tempBuffer[bytesCounter], fDataPointers[tableName][i], fColWidth[tableName][i]);
1076 bytesCounter+=fColWidth[tableName][i];
1077 }
1078 if (fFitsTables[tableName]->WriteRow(tempBuffer, bytesCounter) == false)
1079 *fLog << err << "Error while writing to FITS table " << tableName << endl;
1080 else
1081 fFitsTables[tableName]->FlushNumRows();
1082
1083 delete[] tempBuffer;
1084}
1085Bool_t MWriteFitsFile::ReInit(MParList *pList)
1086{
1087 if (fRule.Length() == 0)
1088 // there is not rule defined. We keep the old file
1089 return MWriteFile::ReInit(pList);
1090
1091 MRead *read = (MRead*)pList->FindTask("MRead");
1092 if (!read)
1093 {
1094 *fLog << err;
1095 *fLog << "ERROR: No Task 'MRead' found in the tasklist. This task is" << endl;
1096 *fLog << " necessary to get the filename. Without a read-filename" << endl;
1097 *fLog << " no output-filename can be created... abort." << endl;
1098 *fLog << endl;
1099 return kFALSE;
1100 }
1101
1102
1103 // close the current files
1104 CloseTopLevelGroup();
1105 for (std::map<TString,ofits*>::iterator it=fFitsTables.begin(); it!=fFitsTables.end(); it++)
1106 {
1107 (it->second)->close();
1108 delete it->second;
1109 }
1110 fFitsTables.clear();
1111 fDataPointers.clear();
1112 fTypeChars.clear();
1113 fColSizes.clear();
1114 fColWidth.clear();
1115 fTableObjectCreated.clear();
1116 fTableHeaderWritten.clear();
1117 DeleteArrayHelper();
1118 fClHelper.clear();
1119
1120 // get new filename
1121 const TString readFileName = read->GetFullFileName();
1122 const TString newname = MWriteRootFile::SubstituteName(fRule, readFileName);
1123
1124 // create new files
1125 OpenTopLevelGroup(newname.Data());
1126 if (!IsFileOpen())
1127 return kFALSE;
1128
1129 if (GetContainer(pList) == kFALSE)
1130 return kFALSE;
1131
1132 // do, what has to be done in ReInit.
1133 return MWriteFile::ReInit(pList);
1134
1135}
1136
1137void MWriteFitsFile::DeleteArrayHelper()
1138{
1139 map<TString, list<MArrayHelperBase *> >::iterator i_helper1 = fClHelper.begin();
1140 while (i_helper1 != fClHelper.end())
1141 {
1142 list<MArrayHelperBase *>::iterator i_helper2 = i_helper1->second.begin();
1143 while(i_helper2 != i_helper1->second.end())
1144 {
1145 delete *i_helper2;
1146
1147 i_helper2++;
1148 }
1149
1150 i_helper1++;
1151 }
1152}
Note: See TracBrowser for help on using the repository browser.