source: trunk/MagicSoft/Mars/mdata/MDataPhrase.cc@ 9338

Last change on this file since 9338 was 8907, checked in by tbretz, 17 years ago
*** empty log message ***
File size: 23.4 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 04/2004 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2008
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MDataPhrase
28//
29// A MDataPhrase is a wrapper for TFormula. It supports access to data-
30// members and/or member functions acessible from the parameter list
31// via MDataMember. It supports access to elements of a MMatrix through
32// the parameter list via MDataElement and it sipports variables set
33// by SetVariables via MDataValue.
34//
35// The parsing is done by TFormula. For more information which functions
36// are supported see TFormula, TFormulaPrimitives and TFormulaMathInterface.
37//
38// The support is done by replacing the parameters with access to the
39// parameter list by parameters like [0], [1],... When evaluating
40// the TFormula first the parameters are evaluated and passed to
41// TFormula. Each parameter is evaluated only once and, if necessary,
42// passed more than once to TFormula. To store the MData* classes used
43// for parameter access a TObjArray is used. Its advantage is, that
44// it has an UncheckedAt function which saves some time, because
45// no obsolete sanity checks must be done accessing the array.
46//
47// Because everything supported by TFormula is also supported by
48// MDataPhrase also conditional expression are supported.
49//
50// For supported functions see TFormulaPrimitive and TMathInterface.
51//
52// Examples:
53//
54// Gaus, Gausn, Landau, Landaun, Pol0-Pol10, Pow2-Pow5
55//
56// In the constructor you can give rule, like
57// "HillasSource.fDist / MHillas.fLength"
58// Where MHillas/HillasSource is the name of the parameter container in
59// the parameter list and fDist/fLength is the name of the data members
60// in the containers. The result will be fDist divided by fLength.
61//
62// In case you want to access a data-member which is a data member object
63// you can acces it with (Remark: it must derive from MParContainer):
64// "MCameraLV.fPowerSupplyA.fVoltagePos5V"
65// (THIS FEATURE IS CURRENTLY NOT SUPPORTED)
66//
67// You can also use parantheses:
68// "HillasDource.fDist / (MHillas.fLength + MHillas.fWidth)"
69//
70// Additional implementations:
71//
72// isnan(x) return 1 if x is NaN (Not a Number) otherwise 0
73// finite(x) return 1 if the number is a valid double (not NaN, inf)
74//
75// NaN (Not a Number) means normally a number which is to small to be
76// stored in a floating point variable (eg. 0<x<1e-56 or similar) or
77// a number which function is not defined (like asin(1.5))
78//
79// inf is the symbol for an infinite number.
80//
81//
82// Constants
83// ---------
84//
85// Most constants you might need can be found in TMath, eg:
86// TMath::Pi(), TMath::TwoPi(), TMath::Ln10(), TMath::LogE()
87// TMath::RadToDeg(), TMath::DegToRad();
88//
89//
90// Variable Parameters
91// ------------------------
92// If you want to use variables, eg for fits you can use [0], [1], ...
93// These values are initialized with 0 and set by calling
94// SetVariables(), eg
95// [0]*MHillas.fArea
96//
97//
98// Multi-argument functions
99// ------------------------
100// You can use multi-argument functions, too. Example:
101// "TMath::Hypot(MHillas.fMeanX, MHillas.MeanY)"
102// "pow(MHillas.fMeanX*MHillas.MeanY, -1.2)"
103//
104//
105//
106// To Do:
107// - The possibility to use other objects inheriting from MData
108// is missing.
109//
110/////////////////////////////////////////////////////////////////////////////
111#include "MDataPhrase.h"
112
113#include <TMath.h>
114#include <TArrayI.h>
115#include <TPRegexp.h>
116#include <TFormula.h>
117
118#include "MLog.h"
119#include "MLogManip.h"
120
121#include "MArrayD.h"
122
123#include "MDataValue.h"
124#include "MDataMember.h"
125#include "MDataElement.h"
126
127ClassImp(MDataPhrase);
128
129using namespace std;
130
131// --------------------------------------------------------------------------
132//
133// Check for the existance of the expression [idx] in the string
134// phrase. If existing a corresponding new MDataValue is added to
135// fMembers and index is increased by one.
136//
137// This makes the use of user-defined variables in the phrase possible.
138//
139Int_t MDataPhrase::CheckForVariable(const TString &phrase, Int_t idx)
140{
141 TString mods;
142 TArrayI pos;
143
144 while (1)
145 {
146 // \\A: matches the beginning of the string like ^ does
147 // \\Z: matches the end of the string like $ does
148 // \\W: matches any non-word character [^a-zA-Z_0-9]
149 TPRegexp reg(Form("(\\A|\\W)\\[0*%d\\](\\W|\\Z)", idx));
150 if (reg.Match(phrase, mods, 0, 130, &pos)==0)
151 break;
152
153 // [idx] already existing. Add a corresponding MDataValue
154 fMembers.AddLast(new MDataValue(0, idx));
155 idx++;
156 }
157
158 return idx;
159}
160
161// --------------------------------------------------------------------------
162//
163// Replace all expressions expr (found by a regular expression \\b%s\\b
164// with %s being the expression) in the string phrase by [idx].
165//
166// The length of [idx] is returned.
167//
168Int_t MDataPhrase::Substitute(TString &phrase, const TString &expr, Int_t idx) const
169{
170 const TString arg = Form("[%d]", idx);
171
172 TPRegexp reg(expr);
173
174 TString mods;
175 TArrayI pos;
176
177 Int_t p = 0;
178 while (1)
179 {
180 // check whether argument is found
181 if (reg.Match(phrase, mods, p, 130, &pos)==0)
182 break;
183
184 // Replace expression by argument [idx]
185 phrase.Replace(pos[0], pos[1]-pos[0], arg, arg.Length());
186
187 // Jump behind the new string which was just inserted
188 p = pos[0]+arg.Length();
189 }
190 return arg.Length();
191}
192
193TString MDataPhrase::Parse(TString phrase)
194{
195 // This is for backward compatibility with MDataChain
196 phrase.ReplaceAll("gRandom->", "TRandom::");
197 phrase.ReplaceAll("kRad2Deg", "TMath::RadToDeg()");
198 phrase.ReplaceAll("kDeg2Rad", "TMath::DegToRad()");
199 phrase.ReplaceAll(" ", "");
200
201 int idx=0;
202 int p =0;
203
204 TString mods;
205 TArrayI pos;
206/*
207 // Find all functions...
208 // The first \\w+ should also allow ::
209 TPRegexp reg = TPRegexp("[[:word:]:]+\\([^()]*\\)");
210 while (1)
211 {
212 //idx = CheckForVariables(phrase, idx);
213
214 if (reg.Match(phrase, mods, p, 130, &pos)==0)
215 break;
216
217 const Int_t len = pos[1]-pos[0];
218
219 // This is the full function with its argument(s)
220 const TString term = phrase(pos[0], len);
221
222 // Now get rid of the argument(s)
223 const TPRegexp reg3("\\([^()]+\\)");
224
225 TString func(term);
226 reg3.Substitute(func, "");
227
228 // Seems to be a special case whic is not handles by
229 // TFormulaPrimitive by known by TFormula
230 if (func.BeginsWith("TRandom::"))
231 {
232 p = pos[0]+pos[1];
233 continue;
234 }
235
236 // check whether the function is available
237 if (TFormulaPrimitive::FindFormula(func))
238 {
239 p = pos[0]+pos[1];
240 continue;
241 }
242
243 cout << "Unknown: " << func << endl;
244 // p = pos[0]+pos[1];
245 return;
246 }
247*/
248
249 p = 0;
250
251 // Find all data-members in expression such as
252 // MTest.fDataMember. Because also floating point
253 // numbers can contain a dot the result has to
254 // be checked carefully
255 // \\w: matches any word character [a-zA-Z_0-9]
256 TPRegexp reg = TPRegexp("\\w+[.]\\w+");
257 TPRegexp ishex("^0x[[:xdigit:]]+$");
258
259 while (1)
260 {
261 // If some indices are already existing
262 // initialize them by a flexible MDataValue
263 idx = CheckForVariable(phrase, idx);
264
265 // Check whether expression is found
266 if (reg.Match(phrase, mods, p, 130, &pos)==0)
267 break;
268
269 // Get expression from phrase
270 const TString expr = phrase(pos[0], pos[1]-pos[0]);
271
272 // Also hex-numbers and floats fullfill our condition...
273 // FIXME: WHY HEX NUMBERS?
274 if (!expr(ishex).IsNull() || expr.IsFloat())
275 {
276 p = pos[1];
277 continue;
278 }
279
280 // Add a corresponding MDataMember to our list
281 fMembers.AddLast(new MDataMember(expr));
282
283 // Find other occurances of arg by this regexp
284 // and start next search behind first match
285 const TString regexp = Form("\\b%s\\b", expr.Data());
286 p = pos[0] + Substitute(phrase, regexp, idx);
287
288 // Step forward to the next argument
289 idx++;
290 }
291
292 p = 0;
293
294 // Now check for matrix elemets as M[5]
295 reg = TPRegexp("\\w+\\[\\d+\\]");
296 while (1)
297 {
298 // If some indices are already existing
299 // initialize them by a MDataValue
300 idx = CheckForVariable(phrase, idx);
301
302 // Check whether expression is found
303 if (reg.Match(phrase, mods, p, 130, &pos)==0)
304 break;
305
306 // Get expression from phrase
307 TString expr = phrase(pos[0], pos[1]-pos[0]);
308
309 // Add a corresponding MDataMember to our list
310 fMembers.AddLast(new MDataElement(expr));
311
312 // Make the expression "Regular expression proofed"
313 expr.ReplaceAll("[", "\\[");
314 expr.ReplaceAll("]", "\\]");
315
316 // Find other occurances of arg by this regexp
317 // and start next search behind first match
318 const TString regexp = Form("\\b%s", expr.Data());
319 p = pos[0] + Substitute(phrase, regexp, idx);
320
321 // Step forward to the next argument
322 idx++;
323 }
324 /*
325
326 // * HOW DO WE PROCESS THE FILTERS?
327 // * DO WE NEED THIS FOR MData derived classes? Is there any need for it?
328 // * MAYBE FIRST FILTERS (MF?) MUST REPLACE {name[class]} BEFORE
329 // THE DATA PHRASSE IS CREATED?
330 // --> MFDataPhrase must have a list of MFilter. In Process first
331 // all self created MFilter are processed (see MF). Then
332 // they are evaluated and the result is given to the MDataPhrase.
333 // Can this be done using MDataMember? We replace {Name[class]}
334 // by Name.IsExpressionTrue and we need a way that MDataPhrase
335 // gets the coresponding pointer.
336 // --> Alternatively we can create a MDataPhrase which allows
337 // Pre/Processing
338 //
339 // We convert {Name[Class]} to Name.IsExpressionTrue. To process these
340 // data/filters we need a request from MFDataPhrase (new virtual
341 // memeber function?)
342 //
343 // {} Is alreaqdy used in ReadEnv.
344 //
345 // Enhance ReadEnv to allow Cut1.5 to be just a class.
346 //
347 // The difference between MFDataPhrase is
348 // MFDataPhrase only knows MDataPhrase, while
349 // MF also knows a filter-class.
350 //
351
352 p = 0;
353
354 // And now we check for other phrases or filters
355 // They are defined by a [, a pribtable character and
356 // any numer of word characters (a-zA-Z0-9_) and a closing ]
357 reg = TPRegexp("{[A-Za-z}\\w+(\\[[A-Za-z]\\w+\\])?}");
358 while (1)
359 {
360 // If some indices are already existing
361 // initialize them by a MDataValue
362 idx = CheckForVariable(phrase, idx);
363
364 // Check whether expression is found
365 if (reg.Match(phrase, mods, p, 130, &pos)==0)
366 break;
367
368 // Get expression from phrase
369 TString expr = phrase(pos[0], pos[1]-pos[0]);
370
371 // Zerlegen: {Name[Class]}
372
373 // Create a new MData object of kind
374 MData *dat = (MData*)GetNewObject(cls, MData::Class());
375 if (!dat)
376 return "";
377 dat->SetName(name);
378
379 // Add a corresponding MDataMember to our list
380 fMembers.AddLast(dat);
381
382 // Make the expression "Regular expression proofed"
383 expr.ReplaceAll("[", "\\[");
384 expr.ReplaceAll("]", "\\]");
385
386 // Find other occurances of arg by this regexp
387 // and start next search behind first match
388 p = pos[0] + Substitute(phrase, expr, idx);
389
390 // Step forward to the next argument
391 idx++;
392 }
393 */
394 // Now we have to check if there are additional indices [idx]
395 // This is mainly important if the rule has indices only!
396 while (1)
397 {
398 const Int_t newidx = CheckForVariable(phrase, idx);
399 if (newidx == idx)
400 break;
401 }
402
403 return phrase;
404}
405
406// --------------------------------------------------------------------------
407//
408// Clear Formula and corresponding data members
409//
410void MDataPhrase::Clear(Option_t *)
411{
412 fMembers.Delete();
413 if (!fFormula)
414 return;
415
416 delete fFormula;
417 fFormula = NULL;
418}
419
420// --------------------------------------------------------------------------
421//
422// Set a new phrase/rule. Returns kTRUE on sucess, kFALSE otherwise
423//
424Bool_t MDataPhrase::SetRule(const TString &rule)
425{
426 Clear();
427
428 const TString txt=Parse(rule);
429 if (txt.IsNull())
430 {
431 Clear();
432 return kFALSE;
433 }
434
435 fFormula = new TFormula;
436
437 // Must have a name otherwise all axis labels disappear like a miracle
438 fFormula->SetName(fName.IsNull()?"TFormula":fName.Data());
439 if (fFormula->Compile(txt))
440 {
441 *fLog << err << dbginf << "Syntax Error: TFormula::Compile failed..."<< endl;
442 *fLog << " Full Rule: " << rule << endl;
443 *fLog << " Parsed Rule: " << txt << endl;
444 Clear();
445 return kFALSE;
446 }
447
448 gROOT->GetListOfFunctions()->Remove(fFormula);
449
450 return kTRUE;
451}
452
453namespace MFastFun {
454 //
455 // Namespace with basic primitive functions registered by TFormulaPrimitive
456 // all function registerd by TFormulaPrimitive can be used in TFormula
457 //
458 Double_t Nint(Double_t x){return TMath::Nint(x);}
459 Double_t Sign(Double_t x){return x<0?-1:+1;}
460 Double_t IsNaN(Double_t x){return TMath::IsNaN(x);}
461 Double_t Finite(Double_t x){return TMath::Finite(x);}
462}
463
464// --------------------------------------------------------------------------
465//
466// Default constructor. Set a rule (phrase), see class description for more
467// details. Set a name and title. If no title is given it is set to the rule.
468//
469MDataPhrase::MDataPhrase(const char *rule, const char *name, const char *title) : fFormula(0)
470{
471 // More in TFormulaPrimitive.cxx
472 // More in TFormulaMathInterface
473 if (!TFormulaPrimitive::FindFormula("isnan"))
474 {
475 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("log2", "log2", (TFormulaPrimitive::GenFunc10)TMath::Log2));
476 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("fabs", "fabs", (TFormulaPrimitive::GenFunc10)TMath::Abs));
477 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("floor", "floor", (TFormulaPrimitive::GenFunc10)TMath::Floor));
478 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("ceil", "ceil", (TFormulaPrimitive::GenFunc10)TMath::Ceil));
479
480 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("nint", "nint", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
481 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("round", "round", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
482 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("sgn", "sgn", (TFormulaPrimitive::GenFunc10)MFastFun::Sign));
483
484 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("isnan", "isnan", (TFormulaPrimitive::GenFunc10)MFastFun::IsNaN));
485 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("finite", "finite", (TFormulaPrimitive::GenFunc10)MFastFun::Finite));
486 }
487
488 // TFormulaPrimitive is used to get direct acces to the function pointers
489 // GenFunc - pointers to the static function
490 // TFunc - pointers to the data member functions
491 //
492 // The following sufixes are currently used, to describe function arguments:
493 // ------------------------------------------------------------------------
494 // G - generic layout - pointer to double (arguments), pointer to double (parameters)
495 // 10 - double
496 // 110 - double, double
497 // 1110 - double, double, double
498
499 fName = name ? name : "MDataPhrase";
500 fTitle = title ? title : rule;
501
502 fMembers.SetOwner();
503
504 if (rule)
505 SetRule(rule);
506}
507
508// --------------------------------------------------------------------------
509//
510// Destructor
511//
512MDataPhrase::~MDataPhrase()
513{
514 if (fFormula)
515 delete fFormula;
516}
517
518// --------------------------------------------------------------------------
519//
520// CopyConstructor
521//
522MDataPhrase::MDataPhrase(MDataPhrase &ts)
523{
524 TFormula *f = ts.fFormula;
525
526 fName = "MDataPhrase";
527 fTitle = f ? f->GetExpFormula() : (TString)"";
528
529 fFormula = f ? (TFormula*)f->Clone() : 0;
530 gROOT->GetListOfFunctions()->Remove(fFormula);
531
532 fMembers.SetOwner();
533
534 TObject *o = NULL;
535 TIter Next(&ts.fMembers);
536 while ((o=Next()))
537 fMembers.AddLast(o->Clone());
538}
539
540// --------------------------------------------------------------------------
541//
542// Evaluates and returns the result of the member list.
543//
544Double_t MDataPhrase::Eval(const Double_t *x) const
545{
546 const Int_t n = fMembers.GetEntriesFast();
547
548 if (fMembers.GetSize()<n)
549 {
550 *fLog << err << "ERROR - MDataPhrase::GetValue: Size mismatch!" << endl;
551 return 0;
552 }
553
554 // This is to get rid of the cost-qualifier for this->fStorage
555 Double_t *arr = fStorage.GetArray();
556
557 // Evaluate parameters (the access with TObjArray::UncheckedAt is
558 // roughly two times faster than with a TIter and rougly three
559 // times than accessing a TOrdCollection. However this might still
560 // be quite harmless compared to the time needed by GetValue)
561 for (Int_t i=0; i<n; i++)
562 arr[i] = static_cast<MData*>(fMembers.UncheckedAt(i))->GetValue();
563
564 // Evaluate function
565 return fFormula->EvalPar(x, arr);
566}
567
568// --------------------------------------------------------------------------
569//
570// Returns kTRUE if all members of fMemebers are valid and fFormula!=NULL
571//
572Bool_t MDataPhrase::IsValid() const
573{
574 TIter Next(&fMembers);
575
576 MData *data = NULL;
577 while ((data=(MData*)Next()))
578 if (!data->IsValid())
579 return kFALSE;
580
581 return fFormula ? kTRUE : kFALSE;
582}
583
584// --------------------------------------------------------------------------
585//
586// Checks whether at least one member has the ready-to-save flag.
587//
588Bool_t MDataPhrase::IsReadyToSave() const
589{
590 TIter Next(&fMembers);
591
592 MData *data = NULL;
593
594 while ((data=(MData*)Next()))
595 if (data->IsReadyToSave())
596 return kTRUE;
597
598 return kFALSE;
599}
600
601// --------------------------------------------------------------------------
602//
603// PreProcesses all members in the list
604//
605Bool_t MDataPhrase::PreProcess(const MParList *plist)
606{
607 if (!fFormula)
608 {
609 *fLog << err << "Error - So far no valid phrase was setup." << endl;
610 return kFALSE;
611 }
612
613 TIter Next(&fMembers);
614
615 MData *member=NULL;
616
617 //
618 // loop over all members
619 //
620 while ((member=(MData*)Next()))
621 if (!member->PreProcess(plist))
622 {
623 *fLog << err << "Error - Preprocessing Data Member ";
624 *fLog << member->GetName() << " in " << fName << endl;
625 return kFALSE;
626 }
627
628 // For speed reasons MArrayD instead of TArrayD is used
629 // (no range check is done when accessing). The storage is
630 // allocated (Set) only when its size is changing. If GetValue
631 // is called many times this should improve the speed significantly
632 // because the necessary memory is already allocated and doesn't need
633 // to be freed. (Just a static variable is not enough, because there
634 // may be several independant objects of this class)
635 fStorage.Set(fMembers.GetSize());
636
637 return kTRUE;
638}
639
640// --------------------------------------------------------------------------
641//
642// Builds a rule from all the list members. This is a rule which could
643// be used to rebuild the list using the constructor of a MDataChain
644//
645TString MDataPhrase::GetRule() const
646{
647 if (!fFormula)
648 return "<empty>";
649
650 TString rule = fFormula->GetTitle(); //fFormula->GetExpFormula();
651
652 MData *member = NULL;
653
654 Int_t i=0;
655 TIter Next(&fMembers);
656 while ((member=(MData*)Next()))
657 {
658 TString r = member->GetRule();
659 r.ReplaceAll("]", "\\]");
660 rule.ReplaceAll(Form("[%d]", i++), r);
661 }
662 rule.ReplaceAll("\\]", "]");
663
664 return rule;
665}
666
667// --------------------------------------------------------------------------
668//
669// This returns the rule as seen/interpreted by the TFormula. Mainly
670// for debugging purposes
671//
672TString MDataPhrase::GetRuleRaw() const
673{
674 if (!fFormula)
675 return "<empty>";
676
677 return fFormula->GetExpFormula();
678}
679
680// --------------------------------------------------------------------------
681//
682// Return the value converted to a Bool_t
683//
684Bool_t MDataPhrase::GetBool() const
685{
686 return TMath::Nint(GetValue())!=0;
687}
688
689/*
690// --------------------------------------------------------------------------
691//
692// Return a comma seperated list of all data members used in the chain.
693// This is mainly used in MTask::AddToBranchList
694//
695TString MDataPhrase::GetDataMember() const
696{
697 TString str;
698
699 TIter Next(&fMembers);
700
701 MData *member=(MData*)Next();
702
703 if (!member->GetDataMember().IsNull())
704 str += member->GetDataMember();
705
706 while ((member=(MData*)Next()))
707 {
708 if (!member->GetDataMember().IsNull())
709 {
710 str += ",";
711 str += member->GetDataMember();
712 }
713 }
714
715 return str;
716}
717*/
718void MDataPhrase::SetVariables(const TArrayD &arr)
719{
720 fMembers.R__FOR_EACH(MData, SetVariables)(arr);
721}
722
723// --------------------------------------------------------------------------
724//
725// Check for corresponding entries in resource file and setup data phrase.
726//
727// Assuming your MDataChain is called (Set/GetName): MyData
728//
729// Now setup the condition, eg:
730// MyData.Rule: log10(MHillas.fSize)
731// or
732// MyData.Rule: log10(MHillas.fSize) - 4.1
733//
734// If you want to use more difficult rules you can split the
735// condition into subrules. Subrules are identified
736// by {}-brackets. Avoid trailing 0's! For example:
737//
738// MyData.Rule: log10(MHillas.fSize) + {0} - {1}
739// MyData.0: 5.5*MHillas.fSize
740// MyData.1: 2.3*log10(MHillas.fSize)
741//
742// The numbering must be continous and start with 0. You can use
743// a subrules more than once. All {}-brackets are simply replaced
744// by the corresponding conditions. The rules how conditions can
745// be written can be found in the class description of MDataChain.
746//
747Int_t MDataPhrase::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
748{
749 Bool_t rc = kFALSE;
750 if (!IsEnvDefined(env, prefix, "Rule", print))
751 return rc;
752
753 TString rule = GetEnvValue(env, prefix, "Rule", "");
754
755 Int_t idx=0;
756 while (1)
757 {
758 TString cond;
759 if (IsEnvDefined(env, prefix, Form("%d", idx), print))
760 {
761 cond += "(";
762 cond += GetEnvValue(env, prefix, Form("%d", idx), "");
763 cond += ")";
764 }
765
766 if (cond.IsNull())
767 break;
768
769 rule.ReplaceAll(Form("{%d}", idx), cond);
770 idx++;
771 }
772
773 if (rule.IsNull())
774 {
775 *fLog << warn << "MDataPhrase::ReadEnv - ERROR: Empty rule found." << endl;
776 return kERROR;
777 }
778
779 if (!SetRule(rule))
780 return kERROR;
781
782 if (print)
783 *fLog << inf << "found: " << GetRule() << endl;
784
785 return kTRUE;
786}
Note: See TracBrowser for help on using the repository browser.