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

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