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

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