9c6b691d119a3b6d82382afbef2c693fe0841db7
[u/mrichter/AliRoot.git] / PWG / Tools / AliLatexTable.cxx
1 // ------------------------------------------------------------------
2 //
3 //                           AliLatexTable
4 //
5 // This class is used to produce tables using latex sintax. It can
6 // than print the table as Latex source code (to be pasted on a paper)
7 // or ASCII.
8 //
9 // The basic idea is that you have a stack of rows. You add columns
10 // one after the other with the SetNextCol method and than you insert
11 // this row. The SetNextCol method comes in different flavours to alow
12 // the insertion of different type of values (numbers with or without
13 // errors, strings...). Errors are rounded and written in scientific
14 // notation with the desired accuracy.
15 //
16 // TODO:
17 // 1. Make the class drawable
18 // 2. Implement vertical lines in ascii print
19 // 3. Improve output in HTML format
20 //
21 // Author: Michele Floris, CERN
22 // ------------------------------------------------------------------
23
24 #include "AliLatexTable.h"
25 #include "TString.h"
26 #include "TRegexp.h"
27 #include "TObjString.h"
28 #include "TObjArray.h"
29 #include <stdarg.h>
30 #include "snprintf.h"
31 #include "Varargs.h"
32 #include "TPRegexp.h"
33 #include "TMath.h"
34 #include <iostream>
35 #include <fstream>
36 #include "AliLog.h"
37
38 using namespace std;
39
40 ClassImp(AliLatexTable)
41
42 AliLatexTable::AliLatexTable() : fNcol(0), fFormat(""), fRows(0), fCols(0), fNcolReady(0){
43   // default constructor
44   fNcol = 1;
45   fFormat = "c";
46   fNcolReady = 0;
47   fRows = new TObjArray;
48   fCols = new TObjArray;
49
50   fRows->SetOwner();
51   fCols->SetOwner();
52
53 }
54
55 AliLatexTable::AliLatexTable(Int_t ncol, TString format) : fNcol(0), fFormat(""), fRows(0), fCols(0), fNcolReady(0){
56   // constructor, specify number of cols 
57   fNcol = ncol;
58   fFormat = format;
59   fNcolReady = 0;
60   fRows = new TObjArray;
61   fCols = new TObjArray;
62
63   fRows->SetOwner();
64   fCols->SetOwner();
65 }
66
67
68 AliLatexTable::~AliLatexTable() {
69
70   // dtor
71   if (fRows) delete fRows;
72   if (fCols) delete fCols;
73
74 }
75
76 void AliLatexTable::SetNextCol(Int_t val){
77   // Set next column in current row - integer
78   char col[200];
79   snprintf(col, 200, " %d ", val);
80   SetNextCol(col);
81
82
83
84 void AliLatexTable::SetNextCol(Int_t val, Int_t err){
85
86   // Set next column in current row - int +- int
87   char col[200];
88   snprintf(col, 200, " $%d \\pm %d$ ", val, err);
89   SetNextCol(col);
90
91
92
93 void AliLatexTable::SetNextCol(Double_t val, Int_t scientificNotation, Bool_t rounding){
94
95   // Set next column in current row - double, specify resolution
96
97   char col[200];
98   if(rounding) {
99     if(scientificNotation >= 0) {
100       char format[100];
101
102       //    cout << format << endl;    
103       Double_t mantissa, exp;
104       GetMantissaAndExpBase10(val, mantissa, exp);
105     
106       if (exp == 0) {
107         snprintf(format, 100," $%%%d.%df $", scientificNotation, scientificNotation);
108         snprintf(col, 200, format, mantissa);      
109       }
110       else  {
111         snprintf(format, 100," $%%%d.%df \\cdot 10^{%%0.0f} $", scientificNotation, scientificNotation);
112         snprintf(col, 200,format, mantissa, exp);
113       }
114
115
116     
117
118       SetNextCol(col);
119
120     } else {
121       char format[100];
122       snprintf(format, 100," $%%%d.%df $", -scientificNotation,-scientificNotation);    
123       snprintf(col, 200, format , TMath::Nint(val*TMath::Power(10,-scientificNotation))/TMath::Power(10,-scientificNotation));
124       SetNextCol(col);
125     } 
126   }else {
127     snprintf(col,200, " %f ", val);
128     SetNextCol(col);
129   }
130
131
132 void AliLatexTable::SetNextCol(Double_t val, Double_t err, Int_t scientificNotation, Bool_t rounding){
133
134   // Set next column in current row - double +- double, specify resolution
135
136   // scientific notation is used to determine number of
137   // digits in error 
138
139   //if it is > 0 exp notation is used 
140
141
142   char col[200];
143   if(rounding) {
144     if(scientificNotation >=0 ) {
145       
146       Double_t mantissa, exp;
147       GetMantissaAndExpBase10(val, mantissa, exp);
148       
149       Double_t mantissa_err, exp_err;
150       GetMantissaAndExpBase10(err, mantissa_err, exp_err);
151
152       Int_t nSigDigits =TMath::Nint(exp - exp_err);
153       if(scientificNotation != 0) nSigDigits =  nSigDigits + scientificNotation - 1;
154
155
156       char format[100];
157       if (exp == 0) {
158         snprintf(format, 100," $%%%d.%df \\pm %%%d.%df $", nSigDigits, nSigDigits, nSigDigits, nSigDigits);
159         snprintf(col, 200,format, mantissa, mantissa_err/TMath::Power(10,exp - exp_err));
160       }
161       else  {
162         snprintf(format, 100, " $%%%d.%df \\pm %%%d.%df \\cdot 10^{%%0.0f}$", nSigDigits, nSigDigits, nSigDigits, nSigDigits);
163         snprintf(col, 200, format, mantissa, mantissa_err/TMath::Power(10,exp - exp_err), exp);
164       }
165
166
167       //cout << format << endl;
168     
169       SetNextCol(col);
170
171     } else  {
172       char format[100];
173       snprintf(format, 100, " $%%%d.%df \\pm %%%d.%df $", -scientificNotation,-scientificNotation,-scientificNotation,-scientificNotation);    
174       snprintf(col, 200, format , TMath::Nint(val*TMath::Power(10,-scientificNotation))/TMath::Power(10,-scientificNotation), TMath::Nint(err*TMath::Power(10,-scientificNotation))/TMath::Power(10,-scientificNotation));
175       SetNextCol(col);
176     }
177   }
178   else {
179     snprintf(col, 200, " $%f \\pm %f$ ", val, err);
180     SetNextCol(col);
181   }
182
183
184 void AliLatexTable::SetNextCol(Double_t val, Double_t err, Double_t errSyst, Int_t scientificNotation, Bool_t rounding){
185
186   // Set next column in current row - double +- double +- double, specify resolution
187
188   // if scientific notation is != -1 is used to determine number of
189   // digits in error 
190
191   //if it is > 0 exp notation is used 
192
193   //if it is < -1 number is truncated to the number of digits after
194   //point dictated by scientificNotation,
195
196   char col[200];
197   if (rounding) {
198     if(scientificNotation >=0 ) {
199
200       Double_t mantissa, exp;
201       GetMantissaAndExpBase10(val, mantissa, exp);
202
203       Double_t mantissa_err, exp_err;
204       GetMantissaAndExpBase10(err, mantissa_err, exp_err);
205
206       Double_t mantissa_errsyst, exp_errsyst;
207       GetMantissaAndExpBase10(errSyst, mantissa_errsyst, exp_errsyst);
208
209       Int_t nSigDigits =TMath::Nint(exp - exp_err);
210       if(scientificNotation != 0) nSigDigits =  nSigDigits + scientificNotation - 1;
211
212
213       char format[100];
214       if (exp == 0) {
215         snprintf(format, 100, " $%%%d.%df \\pm %%%d.%df \\pm %%%d.%df $", 
216                 nSigDigits, nSigDigits, nSigDigits, nSigDigits, nSigDigits, nSigDigits);
217         snprintf(col, 200, format, mantissa, 
218                 mantissa_err/TMath::Power(10,exp - exp_err), 
219                 mantissa_errsyst/TMath::Power(10,exp - exp_errsyst));
220       }
221       else  {
222         snprintf(format, 100, " $%%%d.%df \\pm %%%d.%df  \\pm %%%d.%df \\cdot 10^{%%0.0f}$", 
223                 nSigDigits, nSigDigits, nSigDigits, nSigDigits, nSigDigits, nSigDigits);
224         snprintf(col, 200, format, mantissa, 
225                 mantissa_err/TMath::Power(10,exp - exp_err), 
226                 mantissa_errsyst/TMath::Power(10,exp - exp_errsyst),
227                 exp);
228       }
229
230
231       //cout << format << endl;
232     
233       SetNextCol(col);
234
235     } else  {
236       char format[100];
237       snprintf(format, 100, " $%%%d.%df \\pm %%%d.%df \\pm %%%d.%df $", -scientificNotation,-scientificNotation,-scientificNotation,-scientificNotation, -scientificNotation,-scientificNotation);    
238       snprintf(col, 200, format ,TMath::Nint(val*TMath::Power(10,-scientificNotation))/TMath::Power(10,-scientificNotation), TMath::Nint(err*TMath::Power(10,-scientificNotation))/TMath::Power(10,-scientificNotation), TMath::Nint(errSyst*TMath::Power(10,-scientificNotation))/TMath::Power(10,-scientificNotation));
239       SetNextCol(col);
240     } 
241   }
242   else {
243     snprintf(col, 200, " $%f \\pm %f  \\pm %f$ ", val, err, errSyst);
244     SetNextCol(col);
245   }
246
247
248 // void AliLatexTable::SetNextColPrintf(const char *va_(fmt), ...){
249
250
251 //   const Int_t buf_size = 1024;  // hope it's long enough
252 //   char colBuffer[buf_size];
253
254 //   va_list ap;
255 //   va_start(ap, va_(fmt));
256 //   Int_t n = vsnprintf(colBuffer, buf_size, fmt, ap);
257
258 //   if (n == -1 || n >= buf_size) {
259 //     Error("SetNextCol", "vsnprintf error!");
260 //   }
261
262 //   va_end(ap);
263      
264 // //   for(int iobj=0; iobj<num; iobj++){   //Loop until all objects are added
265 // //     TH1 * obj = (TH1 *) va_arg(arguments, void *);
266 // //     returnValue = returnValue && AddObject(obj,(char*)fOptMany.Data(), EColor(iobj+1));
267 // //   }
268 //   SetNextCol(colBuffer);
269 // }
270
271
272 void AliLatexTable::SetNextCol(TString val){
273
274   // Set next column in current row - tstring
275
276   fCols->Add(new TObjString(val));
277   fNcolReady++;
278
279
280
281 void AliLatexTable::InsertCustomRow(TString row){
282   // insert a full row from string. Make sure you have all the columns
283   fRows->Add(new TObjString(row));
284 }
285
286
287
288 void AliLatexTable::InsertRow(){
289
290   // Insert the row, based on all the individual columns
291
292   if ( fNcolReady != fNcol) {
293     Warning("InsertRow", "Wrong number of cols: %d (!= %d)", fNcolReady, fNcol); 
294   }
295
296   TString row = "";
297   for(Int_t icol = 0; icol < fNcol; icol++){
298     row = row + ((TObjString*) fCols->At(icol))->String();
299     if(icol != (fNcol-1)) row = row + " & ";
300   }
301   row+="\\\\";
302
303   fRows->Add(new TObjString(row));
304
305   fNcolReady = 0;
306   fCols->Clear();
307
308 }
309
310 void AliLatexTable::InsertHline(){
311
312   // insert an horizontal line
313   fRows->Add(new TObjString("\\hline"));
314   
315
316 }
317
318 Int_t * AliLatexTable::GetColWidths() {
319
320   // Compute the width of columns, for nice ascii printing
321
322   Int_t * col_widths= new Int_t[fNcol];
323   Int_t nrow = fRows->GetEntriesFast();
324
325   for(Int_t icol = 0; icol < fNcol; icol++){
326     col_widths[icol] = 0;
327   }
328   
329
330   for(Int_t irow = 0; irow < nrow; irow++){
331     TString row(((TObjString*) fRows->At(irow))->String());
332     if(row.Contains("\\hline"))continue;
333     
334     StripLatex(row, "ASCII");
335
336     TObjArray * cols = row.Tokenize("&");
337     if(cols->GetEntries() != fNcol) {
338       Error("GetColWidths", "Wrong number of cols in row %s: %d - %d", row.Data(), cols->GetEntries(), fNcol);      
339     }
340     for(Int_t icol = 0; icol < fNcol; icol++){
341       Int_t w = ((TObjString *) cols->At(icol))->String().Length();
342       if (w>col_widths[icol]) col_widths[icol] = w;
343     }
344     
345   }
346
347   return col_widths;
348 }
349
350 void AliLatexTable::PrintTable(Option_t * opt){
351
352   // Print the table on screen. You can specify the format with opt.
353   // Currently supported:
354   // "" -> LaTeX
355   // "ASCII" -> plain ASCII
356   // "HTML"  -> HTML, to be improved
357   // "CSV"   -> skips hline, usefult for importing in excell 
358   // "TWIKI" -> skips hline, usefult for importing in TWIKI
359
360   if(TString(opt) == "ASCII" || TString(opt)=="HTML" ||  TString(opt)=="CSV" ||  TString(opt)=="TWIKI") {
361     
362     Int_t nrow = fRows->GetEntriesFast();
363
364     Int_t * colWidths = GetColWidths();
365
366     Int_t total_lenght = 0;
367     for(Int_t icol = 0; icol < fNcol; icol++) total_lenght = total_lenght + colWidths[icol] + 2 ;
368     
369
370     for(Int_t irow = 0; irow < nrow; irow++){
371       TString row = ((TObjString*) fRows->At(irow))->String();
372       if (row.Contains("\\hline")){     
373         if (TString(opt)!="CSV" && TString(opt)!="TWIKI") {
374           for(Int_t il = 0; il < total_lenght; il++) printf("-");
375           printf("\n");   
376         }
377         continue;
378       }
379       StripLatex(row, opt);
380       TObjArray * cols = row.Tokenize("&");
381       if (TString(opt)=="TWIKI") printf(" | ");
382       for(Int_t icol = 0; icol < fNcol; icol++){
383         TString strTmp = ((TObjString *) cols->At(icol))->String();
384         if(TString(opt)=="TWIKI" || TString(opt)=="HTML"){
385           strTmp.ReplaceAll("AMPER","&");
386         }
387         const char * colstr = strTmp.Data();
388         char format [200];
389         if (TString(opt)!="CSV") {
390           snprintf(format, 200, "%%%ds", colWidths[icol] + 2);  
391         } else {
392           snprintf(format, 200, "%%s"); 
393         }
394         printf(format, colstr);
395         if (TString(opt)=="CSV") printf(", ");
396         if (TString(opt)=="TWIKI") printf(" | ");
397
398       }
399       printf ("\n");
400     }
401     
402     delete [] colWidths;
403     return;
404   }
405   
406
407   cout << "\\begin{tabular}{"<<fFormat<<"}"<<endl;
408
409   Int_t nrow = fRows->GetEntriesFast();
410
411   for(Int_t irow = 0; irow < nrow; irow++){
412     cout << ((TObjString*) fRows->At(irow))->String() << endl;
413   }
414   
415   cout << "\\end{tabular}" << endl;
416
417
418 }
419
420 // void AliLatexTable::ParseExponent(TString &expo){
421 // //    TString parseExponent = col;
422 //   TRegexp re = "e[+-][0-9][0-9]";
423 //   //  cout << col << endl;
424   
425 //   if(expo.Contains(re)){
426 //     Int_t index = expo.Index(re);
427 //     TString replacement = "\\cdot 10^{";
428 //     replacement = replacement + expo(index+1, 3) +"}";
429 //     //    cout << replacement <<" --- "<< endl;
430 //     replacement.ReplaceAll("+","");
431 // //     cout << "B: " << expo << endl;      
432 // //     cout << "RE " << expo(re) << endl;
433     
434 //     expo.ReplaceAll(expo(re), replacement);
435 //     //    cout << "A: " << expo << endl;      
436 //     // recursion to parse all exponents
437 //     if (expo.Contains(re)) ParseExponent(expo);
438 //   }
439 //   else Warning("", "Error parsing exponent");
440 // }
441
442 void AliLatexTable::GetMantissaAndExpBase10(Double_t num, Double_t &man, Double_t &exp) {
443
444   // Helper used to get mantissa and exponent
445
446   exp = TMath::Floor(TMath::Log10(TMath::Abs(num)));  
447   man = num / TMath::Power(10, exp);
448
449 //   cout << "" << endl;
450 //   cout << num << " = " << man << " x10^{"<<exp<<"} "   << endl;
451   
452
453 }
454
455 void AliLatexTable::StripLatex(TString &text, TString format) {
456
457   // Strip latex away for ascii and html printing. Replaces latex
458   // command with corresponding text/tags
459
460   text.ReplaceAll("\\cdot", "x");
461   text.ReplaceAll("$", "");
462   if (format == "ASCII") {
463     text.ReplaceAll("\\right>", ">");
464     text.ReplaceAll("\\left<", "<");
465     text.ReplaceAll("\\rangle", ">");
466     text.ReplaceAll("\\langle", "<");
467     text.ReplaceAll("\\pm", "+-");
468   } else if (format == "HTML" || format == "TWIKI") {
469     // the & is used to tokenize... Have to cheat here
470     text.ReplaceAll("\\right>", "AMPERrang;");
471     text.ReplaceAll("\\left<",  "AMPERlang;");
472     text.ReplaceAll("\\rangle", "AMPERrang;");
473     text.ReplaceAll("\\langle", "AMPERlang;");
474     text.ReplaceAll("\\pm",     "AMPERplusmn;");
475   } 
476   if(text.Contains("multicolumn")) {
477     //    cout << "col " << text.Data() << endl;
478     // in case of multicol span, replace first column with content and
479     // add n empty cols
480     TObjArray * array = TPRegexp("multicolumn\\{([^}]*)\\}\\{[^}]*\\}\\{([^]]*)\\}").MatchS(text); // Added double \\ because gcc 4 triggers hard error on unknown escape sequence. Hope it still works...
481     const TString content = ((TObjString *)array->At(2))->GetString();
482     Int_t nspan   = ((TObjString *)array->At(1))->GetString().Atoi();
483     text = content;
484     // cout << "ns " << nspan <<  ((TObjString *)array->At(1))->GetString() << endl;
485     // cout << "t " << text << endl;
486     
487     for(Int_t ispan = 1; ispan < nspan; ispan++){
488       text+=" & ";
489     }
490     //    cout << "t " << text << endl;
491     
492   
493   }
494
495   text.ReplaceAll("\\mathrm", "");
496
497   text.ReplaceAll("\\", "");
498   text.Strip(TString::EStripType(1), ' ');
499
500 }
501
502 void AliLatexTable::LoadTeXFromFileAndPrintASCII(const char * filename) {
503
504   // opens a file containing only a latex table and prints it on screen as ASCII
505
506   ifstream file (filename);
507   if (!file.is_open()) {
508     AliError(Form("Cannot open file %s", filename));
509   }
510   TString line;
511   while (line.ReadLine(file)) {
512     if (line.Contains("begin") && line.Contains("tabular")) {
513       // We need to get and parse the format
514       //      TPRegexp re("\\begin\\{tabular\\}\\{([^\\}]*)\\}");
515       TPRegexp re(".*begin{tabular}{(.*)}");
516       TObjArray * arr = re.MatchS(line);
517       if (arr->GetLast() > 0){ 
518         //      cout << "Size: " << arr->GetSize() << " " << arr->GetLast() << endl;
519         
520         TString subStr = ((TObjString *)arr->At(1))->GetString();
521         subStr.ReplaceAll("|","");
522         subStr.ReplaceAll(" ","");
523         subStr.ReplaceAll("\t","");
524         //      subStr.ReplaceAll(" ","");      
525         //      cout << subStr.Data() << " " << subStr.Length()<< endl;
526         fNcol = subStr.Length();
527         delete arr;
528       }
529     }
530
531     // Skip stuff we don't parse
532     if (line.Contains("begin")) continue;
533     if (line.Contains("end")) continue;
534     if (line.Contains("tabular")) continue;
535     // add line
536     InsertCustomRow(line.Data());
537     
538   }
539   PrintTable("ASCII");
540 }