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

Last change on this file since 15112 was 14935, checked in by tbretz, 12 years ago
Add changes necessary to remove fits completely from namespace std in MARS.
File size: 41.5 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}
339
340
341// --------------------------------------------------------------------------
342//
343// Opens all FITS files for all container, which were registered with the
344// AddContainer() - methods. The container is taken from the pList, if it
345// was registered just by its name.
346// Calling the AddContainer() after calling this method will not add new
347// containers to the list.
348//
349Bool_t MWriteFitsFile::GetContainer(MParList *pList)
350{
351 if (iTopFitsGroup == fTopFitsGroups.end())
352 // something went wrong while the top level group was created
353 return kFALSE;
354
355
356 // remove the extension from the filename
357 char fileNameNoExt[strlen(GetFileName()) + 1];
358 strcpy(fileNameNoExt, GetFileName());
359 char * pos = strrchr(fileNameNoExt, '.');
360 if (pos) *pos = 0;
361
362 // loop over all FITS tables which have to be created.
363 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
364 fSubTables.begin();
365 while (i_table != fSubTables.end())
366 {
367
368 ofits* fitsTable = new ofits();
369 fitsTable->AllowCommentsTrimming(true);
370 TString dol = fileNameNoExt;
371 //get rid of the ".root" extension in the file name (if any)
372 if (dol(dol.Length()-5, dol.Length()) == ".root")
373 {
374 dol = dol(0, dol.Length()-5);
375// *fLog << err << "New name: " << dol.Data() << endl;
376 }
377// else
378// *fLog << err << dol(dol.Length()-5, dol.Length()).Data() << endl;
379 dol += "_";
380 dol += i_table->first;
381 dol += ".fits";
382 fitsTable->open(dol.Data());
383 *fLog << inf << "Opening FITS file: " << dol.Data() << endl;
384 fFitsTables[i_table->first] = fitsTable;
385 fTableObjectCreated[i_table->first] = true;
386 fTableHeaderWritten[i_table->first] = false;
387
388 // loop over all containers, which define a sub-table of the current table
389 Int_t num = 0;
390 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
391 while (i_subTable != i_table->second.end())
392 {
393
394 MFitsSubTable & subTable = i_subTable->second;
395 if (subTable.GetContainer() == NULL)
396 {
397 // container address is not yet known
398 const char * cname = i_subTable->first.Data();
399 MParContainer *cont = (MParContainer*)pList->FindObject(cname);
400 if (!cont)
401 {
402 // no corresponding container is available in pList
403 if (subTable.MustHave())
404 {
405 *fLog << err << "Cannot find parameter container '" << cname << "'." << endl;
406 return kFALSE;
407 }
408
409 // we can ignore this container, delete it from the map
410 *fLog << inf2 << "Unnecessary parameter container '" << cname << "' not found..." << endl;
411 map<TString, MFitsSubTable>::iterator i_tmp = i_subTable;
412 i_subTable++;
413 i_table->second.erase(i_tmp);
414 continue;
415 }
416
417 // we have our container address and can use it
418 subTable.SetContainer(cont);
419 }
420
421 // initialize all columns of the sub-table, defined by the current container
422 TClass * cl = i_subTable->second.GetContainer()->IsA();
423 if (!InitColumns(i_table->first, TString(cl->GetName()) + ".", fitsTable,
424 i_subTable->second.GetContainer(), cl) )
425 return kFALSE;
426
427 char attrName[10];
428 sprintf(attrName, "CLNAME%d", num);
429 num++;
430
431 InitAttr(attrName,
432 "A",
433 (void*)i_subTable->first.Data(),
434 NULL,
435 "MARS container name",
436 fitsTable);
437
438 i_subTable++;
439 }
440
441 // in case not all sub-tables were removed, we can now create the FITS table
442 if (i_table->second.size() > 0)
443 {
444 // create the DOL of the table. It will be something like:
445 // fileNameNoExt_dataName.fits[dataName]
446 TString dol = fileNameNoExt;
447 dol += "_";
448 dol += i_table->first;
449 dol += ".fits";
450
451 if (fOpenOption == "RECREATE")
452 // remove the file
453 unlink(dol.Data());
454
455 dol += "[";
456 dol += i_table->first;
457 dol += "]";
458// *fLog << err << "Reiner would have opened fits file: " << dol.Data() << endl;
459 //exit(0);
460 //fitsTable->open(dol.Data());
461
462 // attach the new table to the top level group
463 //TODO make sure that this attach below is not needed...
464 //iTopFitsGroup->second.Attach(fitsTable);
465 }
466
467 i_table++;
468 }
469
470 return kTRUE;
471}
472void MWriteFitsFile::InitAttr(const char* attrName,
473 const char* dataType,
474 void* var,
475 const char* unit,
476 const char* comment,
477 ofits* outFile)
478{
479 if (outFile == NULL)
480 return;
481 string dts(dataType);
482 string ans(attrName);
483 string cs;
484 if (comment != NULL)
485 cs = string(comment);
486 else
487 cs = "";
488 ostringstream val;
489 if ((dts == "bool") || (dts == "Bool_t") || (dts == "L"))
490 {
491 outFile->SetBool(ans, ((bool*)(var))[0], cs);
492 return;
493 }
494 if ((dts == "char") || (dts == "Char_t") || (dts == "S"))
495 {
496 val << ((char*)(var))[0];
497 outFile->SetStr(ans, val.str(), cs);
498 return;
499 }
500 if ((dts == "unsigned char") || (dts == "UChar_t") || (dts == "B"))
501 {
502 val << ((unsigned char*)(var))[0];
503 outFile->SetStr(ans, val.str(), cs);
504 return;
505 }
506 if ((dts == "short") || (dts == "Short_t") || (dts == "I"))
507 {
508 val << ((short*)(var))[0];
509 outFile->SetStr(ans, val.str(), cs);
510 return;
511 }
512 if ((dts == "unsigned short") || (dts == "UShort_t") || (dts == "U"))
513 {
514 val << ((unsigned short*)(var))[0];
515 outFile->SetStr(ans, val.str(), cs);
516 return;
517 }
518 if ((dts == "int") || (dts == "Int_t") || (dts == "V"))
519 {
520 outFile->SetInt(ans, ((int*)(var))[0], cs);
521 return;
522 }
523 if ((dts == "unsigned int") || (dts == "UInt_t") || (dts == "V"))
524 {
525 outFile->SetInt(ans, ((unsigned int*)(var))[0], cs);
526 return;
527 }
528 if ((dts == "long long") || (dts == "Long64_t") || (dts == "K"))
529 {
530 outFile->SetInt(ans, ((long long*)(var))[0], cs);
531 return;
532 }
533 if ((dts == "unsigned long long") || (dts == "ULong64_t") || (dts == "W"))
534 {
535 val << ((unsigned long long*)(var))[0];
536 outFile->SetStr(ans, val.str(), cs);
537 return;
538 }
539 if ((dts == "float") || (dts == "TFloat_t") || (dts == "E"))
540 {
541 outFile->SetFloat(ans, ((float*)(var))[0], cs);
542 return;
543 }
544 if ((dts == "double") || (dts == "TDouble_t") || (dts == "D"))
545 {
546 outFile->SetFloat(ans, ((double*)(var))[0], cs);
547 return;
548 }
549 if ((dts == "char*") || (dts == "A"))
550 {
551 outFile->SetStr(ans, string((char*)(var)), cs);
552 return;
553 }
554 //trigger error
555 *fLog << err << "Format string not recognized while adding header entry " << ans << " with type " << dts << endl;
556}
557// --------------------------------------------------------------------------
558//
559// This method is called when new data should be written to the FITS table.
560// In general only one row is written. (There are exceptions for Arrays).
561// A new row to a FITS table is written if at least for one container
562// (one sub-table) the ReadyToSave - flag is set.
563//
564Bool_t MWriteFitsFile::CheckAndWrite()
565{
566// try {
567
568 // loop over all tables
569 map<TString, map<TString, MFitsSubTable> >::iterator i_table =
570 fSubTables.begin();
571 while (i_table != fSubTables.end())
572 {
573 // is this table open?
574 if (fFitsTables.find(i_table->first) != fFitsTables.end())
575 {
576
577 // loop over all sub-tables
578 map<TString, MFitsSubTable>::iterator i_subTable = i_table->second.begin();
579 while (i_subTable != i_table->second.end())
580 {
581
582 if (i_subTable->second.GetContainer()->IsReadyToSave())
583 {
584 // first write the TClonesArray and set the size of the arrays
585 list<MArrayHelperBase*> & clHelper = fClHelper[i_table->first];
586 list<MArrayHelperBase*>::iterator i_clHelper = clHelper.begin();
587 while (i_clHelper != clHelper.end())
588 {
589 // write all entries in the TClonesArray in its FITS table
590 //removed this write because I do it elsewhere. is that alright ?
591 // (*i_clHelper)->Write();
592 i_clHelper++;
593 }
594
595 // write one line to this table
596 writeOneRow(i_table->first);
597// fFitsTables[i_table->first].Write();
598 break;
599 }
600 i_subTable++;
601 }
602 }
603
604 i_table++;
605 }
606// }
607// catch (exception &e)
608 // {
609 // *fLog << err << e.what() << endl;
610 // return kFALSE;
611 // }
612
613 return kTRUE;
614}
615
616string MWriteFitsFile::Trim(const string &str)
617{
618 // Trim Both leading and trailing spaces
619 const size_t first = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
620 const size_t last = str.find_last_not_of(' '); // Find the first character position from reverse af
621
622 // if all spaces or empty return an empty string
623 if (string::npos==first || string::npos==last)
624 return string();
625
626 return str.substr(first, last-first+1);
627}
628
629Bool_t MWriteFitsFile::VetoColumn(const std::string& colName)
630{
631 for (std::vector<string>::iterator it=fVetoedColumns.begin(); it != fVetoedColumns.end(); it++)
632 if (*it == colName)
633 {
634 *fLog << warn << "Warning: column " << colName << " is being vetoed twice" << endl;
635 return kFALSE;
636 }
637 fVetoedColumns.push_back(colName);
638 return kTRUE;
639}
640
641Bool_t MWriteFitsFile::SetBytesPerSample(const std::string& colName, uint32_t numBytes)
642{
643 for (map<string, uint32_t>::iterator it=fBytesPerSamples.begin(); it!=fBytesPerSamples.end(); it++)
644 if (it->first == colName)
645 {
646 *fLog << warn << "Warning: column " << colName << " bytes per sample is being redefined twice" << endl;
647 return kFALSE;
648 }
649 if (numBytes != 1 && numBytes != 2 && numBytes != 4 && numBytes != 8)
650 {
651 *fLog << warn << "Only powers of two are allowed for types re-mapping." << endl;
652 return kFALSE;
653 }
654 fBytesPerSamples[colName] = numBytes;
655 return kTRUE;
656}
657
658// --------------------------------------------------------------------------
659//
660// Initialize all columns in "fitsTable" of the class "classDef". The data
661// of this class are stored in a buffer, beginning at "baseAdr".
662//
663Bool_t MWriteFitsFile::InitColumns(const TString & tableName,
664 const TString & parentVarName,
665 ofits* fitsTable,
666 void * baseAdr,
667 TClass * classDef)
668{
669 // get all data members of the class
670 TList * dataMembers = classDef->GetListOfDataMembers();
671 TIter next(dataMembers);
672 TDataMember * dataMember;
673
674 // loop over all data members
675 while ((dataMember = (TDataMember*)next()) != NULL)
676 {
677 if (!dataMember->IsPersistent())
678 // don't store this variable
679 continue;
680
681 if (dataMember->Property() & ( G__BIT_ISENUM | G__BIT_ISSTATIC))
682 // we cannot store this
683 continue;
684
685 if (strcmp(dataMember->GetTrueTypeName(), "TClass*") == 0)
686 // we don't want to store this.
687 continue;
688
689 // is it an array of more than 1 dimension?
690 if (dataMember->GetArrayDim() > 1)
691 {
692 *fLog << err << "Two and more dimensional arrays of member variables"
693 " are not supported." << endl;
694 *fLog << "See variable " << dataMember->GetName() <<
695 " in container " << classDef->GetName() << endl;
696 return kFALSE;
697 }
698
699
700 // replace � by **2 in the comment field
701 string comment(dataMember->GetTitle());
702 string::size_type pos1, pos2;
703 if ((pos1 = comment.find(178)) != string::npos)
704 comment.replace(pos1, 1, "**2");
705
706 // get the optional mapping to the fits column names
707 string fitsOptions="";
708 string unit="unit";
709
710 pos1 = comment.find("{fits: ");
711 pos2 = comment.find('}');
712 std::map<string, string> fitsTokens;
713 if (pos1 != string::npos && pos2 != string::npos)
714 {
715 fitsOptions=comment.substr(pos1+7, (pos2-pos1)-7);
716 //do we have more than one option ?
717 string::size_type pos3= fitsOptions.find_first_of(';');
718 string::size_type pos4 = string::npos;
719 string key="";
720 string value="";
721 string keyValue="";
722 while (pos3 != string::npos)
723 {//we have at least 2 options left
724 // *fLog << err << "fitsOptions: " << fitsOptions << endl;
725 keyValue = fitsOptions.substr(0,pos3);
726 // *fLog << err << "keyValue: " << keyValue << endl;
727 pos4 = keyValue.find('=');
728 if (pos4 == string::npos)
729 {
730 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
731 return kFALSE;
732 }
733 key = Trim(keyValue.substr(0, pos4));
734 value = Trim(keyValue.substr(pos4+1, pos3));
735 fitsTokens[key] = value;
736 // *fLog << err << "key: " << key << " value: " << value << endl;
737 fitsOptions = fitsOptions.substr(pos3+1, fitsOptions.size());
738 pos3 = fitsOptions.find_first_of(';');
739 }
740// *fLog << err << "fitsOptions: " << fitsOptions << endl;
741 keyValue = fitsOptions;
742 pos4 = keyValue.find('=');
743 if (pos4 == string::npos)
744 {
745 *fLog << err << "Error while parsing comment \"" << comment << "\" from variable " << parentVarName + dataMember->GetName() << endl;
746 return kFALSE;
747 }
748 key = Trim(keyValue.substr(0, pos4));
749 value = Trim(keyValue.substr(pos4+1, pos3));
750 fitsTokens[key] = value;
751// *fLog << err << "key: " << key << " value: " << value << endl;
752 }
753
754 TString colName = parentVarName + dataMember->GetName();
755
756 if (fitsTokens.find("unit") != fitsTokens.end())
757 unit = fitsTokens["unit"];
758 if (fitsTokens.find("name") != fitsTokens.end())
759 colName = fitsTokens["name"];
760
761 //check for column veto
762 if (std::find(fVetoedColumns.begin(), fVetoedColumns.end(), colName.Data())!=fVetoedColumns.end())
763 {
764 *fLog << inf << "Vetoing column " << colName.Data() << endl;
765 continue;
766 }
767
768 // set the array size
769 TString dataType = dataMember->GetTrueTypeName();
770
771 uint32_t dataLength = 0;
772 switch (dataMember->GetArrayDim())
773 {
774 case 0:
775 dataLength = 1;
776 break;
777 case 1:
778 dataLength = dataMember->GetMaxIndex(0);
779 break;
780 default:
781 *fLog << err << "n-dimensional array should have been discarded already " << colName.Data();
782 break;
783 };
784// if (dataLength == 0)
785// continue;
786
787 if (dataMember->Property() & G__BIT_ISCLASS)
788 {
789 // special treatment for classes
790 uint32_t arraySize = 0;
791 TString typeName;
792
793 if (strncmp(dataMember->GetTrueTypeName(), "MArray", 6) == 0)
794 {
795// MArrayHelperBase * clHelper;
796 if (strcmp(dataMember->GetTrueTypeName(), "MArrayS*") == 0)
797 {
798 typeName = "UShort_t";
799 arraySize = (*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
800 InitSingleColumn(tableName,
801 arraySize,
802 typeName.Data(),
803 (char*)(*((MArrayS**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
804 colName.Data(),
805 unit,
806 comment);
807 }
808 else
809 {
810 if (strcmp(dataMember->GetTrueTypeName(), "MArrayB*") == 0)
811 {
812 typeName = "UChar_t";
813 arraySize = (*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetSize();
814 InitSingleColumn(tableName,
815 arraySize,
816 typeName.Data(),
817 (char*)(*((MArrayB**)((char*)baseAdr + dataMember->GetOffset())))->GetArray(),
818 colName.Data(),
819 unit,
820 comment);
821 }
822 else
823 {
824 *fLog << err << dataMember->GetTrueTypeName() << " not yet implemented." << endl;
825 return kFALSE;
826 }
827 }
828
829 continue;
830 }
831 else if (strcmp(dataMember->GetTrueTypeName(), "TClonesArray") == 0)
832 {
833 *fLog << warn << "I'm skipping the TClonesArray for now" << endl;
834 continue;
835 // each TClonesArray requires a FITS table by itself.
836 MClonesArrayHelper * clHelper;
837
838 TClonesArray * cloneArray = (TClonesArray*)((char*)baseAdr + dataMember->GetOffset());
839 Bool_t status;
840 clHelper = new MClonesArrayHelper(cloneArray, fLog, status);
841 if (!status) return status;
842
843 fClHelper[tableName].push_back(clHelper);
844
845 // add one column in the parent table of the TClonesArray to store the
846 // number of entries in the TClonesArray.
847 InitSingleColumn(tableName,
848 1,
849 "UInt_t",
850 clHelper->GetArraySizePtr(),
851 colName.Data(),
852 unit,
853 comment);
854
855 // initialize the columns of the new FITS table, which will store the
856 // data entries of the TClonesArray
857 if (InitColumns(TString("noName"),
858 colName + "_",
859 fitsTable,
860 clHelper->GetDataBuffer(),
861 cloneArray->GetClass())
862 == kFALSE)
863 return kFALSE;
864
865 // the columns are initialized. We can create the FITS table
866 if (clHelper->OpenFitsTable(GetFileName(), dataMember->GetName(),
867 fOpenOption, fLog) == kFALSE)
868 return kFALSE;
869 }
870
871 else
872 {
873 // the current container has a variable of an other class. We create
874 // also columns of this other class in the same table
875 TClass * newClassDef = TClass::GetClass(dataMember->GetTrueTypeName(), kFALSE, kTRUE);
876 if (newClassDef)
877 {
878 if (InitColumns(tableName, colName + ".", fitsTable, (char*)baseAdr + dataMember->GetOffset(),
879 newClassDef) == kFALSE)
880 return kFALSE;
881 }
882 else
883 *fLog << warn << "Cannot write data of class " << colName + "." + dataMember->GetTrueTypeName() << endl;
884 }
885 continue;
886 }
887
888 InitSingleColumn(tableName,
889 dataLength,
890 dataType.Data(),
891 (char*)baseAdr + dataMember->GetOffset(),
892 colName.Data(),
893 unit,
894 comment);
895
896 }
897 return kTRUE;
898}
899void MWriteFitsFile::InitSingleColumn(const TString& tableName,
900 uint32_t count,
901 const string& typeName,
902 void* dataPointer,
903 const string& columnName,
904 const string& unit,
905 const string& comment)
906{
907 if (!fTableObjectCreated[tableName])
908 {
909 *fLog << err << "ERROR: Cannot init column " << columnName << " before assigning object: " << tableName << endl;
910 return;
911 }
912 int typeFound = 0;
913 char typeChar = '0';
914 if ((typeName == "bool") || (typeName == "Bool_t") || (typeName == "L"))
915 {
916 typeChar = 'L';
917 typeFound++;
918 }
919 if ((typeName == "char") || (typeName == "Char_t") || (typeName == "S"))
920 {
921 typeChar = 'A';
922 typeFound++;
923 }
924 if ((typeName == "unsigned char") || (typeName == "UChar_t") || (typeName == "B"))
925 {
926 typeChar = 'A';
927// *fLog << warn << "Converting unsigned char to char in fits file" << endl;
928 typeFound++;
929 }
930 if ((typeName == "short") || (typeName == "Short_t") || (typeName == "I"))
931 {
932 typeChar = 'I';
933 typeFound++;
934 }
935 if ((typeName == "unsigned short") || (typeName == "UShort_t") || (typeName == "U"))
936 {
937 typeChar = 'I';
938// *fLog << warn << "Converting unsigned short to short in fits file" << endl;
939 typeFound++;
940 }
941 if ((typeName == "int") || (typeName == "Int_t") || (typeName == "V"))
942 {
943 typeChar = 'J';
944 typeFound++;
945 }
946 if ((typeName == "unsigned int") || (typeName == "UInt_t") || (typeName == "V"))
947 {
948 typeChar = 'J';
949// *fLog << warn << "Converting unsigned int to int in fits file" << endl;
950 typeFound++;
951 }
952 if ((typeName == "long long") || (typeName == "Long64_t") || (typeName == "K"))
953 {
954 typeChar = 'K';
955 typeFound++;
956 }
957 if ((typeName == "unsigned long long") || (typeName == "ULong64_t") || (typeName == "W"))
958 {
959 typeChar = 'K';
960// *fLog << warn << "Converting unsigned long to long in fits file" << endl;
961 typeFound++;
962 }
963 if ((typeName == "float") || (typeName=="TFloat_t") || (typeName == "E"))
964 {
965 typeChar = 'E';
966 typeFound++;
967 }
968 if ((typeName == "double") || (typeName == "TDouble_t") || (typeName == "D"))
969 {
970 typeChar = 'D';
971 typeFound++;
972 }
973// if ((typeName == "char*") || (typeName == "A"))
974// {
975// typeFound++;
976// }
977 if (typeFound != 1)
978 {
979 *fLog << err << "We have a problem with the data type: " << typeName << endl;
980 return;
981 }
982 uint32_t colWidth = 0;
983 switch (typeChar)
984 {
985 case 'L': colWidth = 1*count; break;
986 case 'A': colWidth = 1*count; break;
987 case 'B': colWidth = 1*count; break;
988 case 'I': colWidth = 2*count; break;
989 case 'J': colWidth = 4*count; break;
990 case 'K': colWidth = 8*count; break;
991 case 'E': colWidth = 4*count; break;
992 case 'D': colWidth = 8*count; break;
993 default:
994 *fLog << err << "ERROR: typeChar could not be resolved to an actual type" << endl;
995 };
996 //check for type remapping here
997 if (fBytesPerSamples.find(columnName) != fBytesPerSamples.end())
998 {
999 if (typeChar != 'A')
1000 {
1001 *fLog << err << "Attempt to remap type " << typeChar << " to " << fBytesPerSamples[columnName] << " is only allowed on bytes (variable name: " << columnName << "). Ignoring column" << endl;
1002 return;
1003 }
1004 uint32_t bytesPerSample = fBytesPerSamples.find(columnName)->second;
1005 if (colWidth%bytesPerSample != 0)
1006 {
1007 *fLog << err << "Type remapping cannot be done using " << bytesPerSample << " bytes per sample on an array of char of size " << colWidth << ". Ignoring column " << columnName << endl;
1008 return;
1009 }
1010 switch (bytesPerSample)
1011 {
1012 case 1: count = count/1; typeChar = 'A'; break;
1013 case 2: count = count/2; typeChar = 'I'; break;
1014 case 4: count = count/4; typeChar = 'J'; break;
1015 case 8: count = count/8; typeChar = 'K'; break;
1016 default:
1017 *fLog << err << "ERROR: num bytes per sample = " << bytesPerSample << " should have been forbidden already" << endl;
1018 }
1019
1020 }
1021
1022 fDataPointers[tableName].push_back(dataPointer);
1023 fTypeChars[tableName].push_back(typeChar);
1024 fColSizes[tableName].push_back(count);
1025 fColWidth[tableName].push_back(colWidth);
1026
1027 //FIXME ofits does not allow for much liberty regarding the size of the column names.
1028 //Truncating them badly here, will probably cause other problems -> Modify ofits.h instead
1029 string truncatedName=columnName.substr((columnName.size()>40)?columnName.size()-40:0,columnName.size());
1030 string truncatedComment = comment.substr((comment.size()>10)?comment.size()-10:0,comment.size());
1031// *fLog << warn << "In table " << tableName << " Adding column |" << truncatedName << "| |" << truncatedComment << "| |" << count << "| |" << typeChar;
1032// *fLog << warn << "| Real: "<< columnName << " comment: " << comment << endl;
1033 fFitsTables[tableName]->AddColumn(count, typeChar, truncatedName, unit, truncatedComment);
1034}
1035
1036void MWriteFitsFile::writeOneRow(const TString& tableName)
1037{
1038 if (!fTableHeaderWritten[tableName])
1039 {
1040 fFitsTables[tableName]->WriteTableHeader(tableName.Data());
1041 fTableHeaderWritten[tableName] = true;
1042 }
1043 if (!fTableObjectCreated[tableName])
1044 {
1045 *fLog << err << "This is not good. Please initialize the fits table before writing to it: " << tableName << endl;
1046 return;
1047 }
1048 //first calculate the size of one row
1049 uint32_t rowWidth = 0;
1050 for (uint32_t i=0;i<fTypeChars[tableName].size();i++)
1051 rowWidth += fColWidth[tableName][i];
1052 unsigned char* tempBuffer = new unsigned char[rowWidth];
1053 //then copy the data to be written contiguously
1054 uint32_t bytesCounter = 0;
1055 for (uint32_t i=0;i<fDataPointers[tableName].size();i++)
1056 {
1057 memcpy(&tempBuffer[bytesCounter], fDataPointers[tableName][i], fColWidth[tableName][i]);
1058 bytesCounter+=fColWidth[tableName][i];
1059 }
1060 if (fFitsTables[tableName]->WriteRow(tempBuffer, bytesCounter) == false)
1061 *fLog << err << "Error while writing to FITS table " << tableName << endl;
1062 else
1063 fFitsTables[tableName]->FlushNumRows();
1064
1065 delete[] tempBuffer;
1066}
1067Bool_t MWriteFitsFile::ReInit(MParList *pList)
1068{
1069 if (fRule.Length() == 0)
1070 // there is not rule defined. We keep the old file
1071 return MWriteFile::ReInit(pList);
1072
1073 MRead *read = (MRead*)pList->FindTask("MRead");
1074 if (!read)
1075 {
1076 *fLog << err;
1077 *fLog << "ERROR: No Task 'MRead' found in the tasklist. This task is" << endl;
1078 *fLog << " necessary to get the filename. Without a read-filename" << endl;
1079 *fLog << " no output-filename can be created... abort." << endl;
1080 *fLog << endl;
1081 return kFALSE;
1082 }
1083
1084
1085 // close the current files
1086 CloseTopLevelGroup();
1087 for (std::map<TString,ofits*>::iterator it=fFitsTables.begin(); it!=fFitsTables.end(); it++)
1088 {
1089 (it->second)->close();
1090 delete it->second;
1091 }
1092 fFitsTables.clear();
1093 fDataPointers.clear();
1094 fTypeChars.clear();
1095 fColSizes.clear();
1096 fColWidth.clear();
1097 fTableObjectCreated.clear();
1098 fTableHeaderWritten.clear();
1099 DeleteArrayHelper();
1100 fClHelper.clear();
1101
1102 // get new filename
1103 const TString readFileName = read->GetFullFileName();
1104 const TString newname = MWriteRootFile::SubstituteName(fRule, readFileName)+".fits";
1105
1106 // create new files
1107 OpenTopLevelGroup(newname.Data());
1108 if (!IsFileOpen())
1109 return kFALSE;
1110
1111 if (GetContainer(pList) == kFALSE)
1112 return kFALSE;
1113
1114 // do, what has to be done in ReInit.
1115 return MWriteFile::ReInit(pList);
1116
1117}
1118
1119void MWriteFitsFile::DeleteArrayHelper()
1120{
1121 map<TString, list<MArrayHelperBase *> >::iterator i_helper1 = fClHelper.begin();
1122 while (i_helper1 != fClHelper.end())
1123 {
1124 list<MArrayHelperBase *>::iterator i_helper2 = i_helper1->second.begin();
1125 while(i_helper2 != i_helper1->second.end())
1126 {
1127 delete *i_helper2;
1128
1129 i_helper2++;
1130 }
1131
1132 i_helper1++;
1133 }
1134}
Note: See TracBrowser for help on using the repository browser.