source: branches/AddingGoogleTestEnvironment/mfbase/MF.cc@ 18302

Last change on this file since 18302 was 9935, checked in by tbretz, 14 years ago
Accept = as conditional sign and make sure that the return code of ReadEnv of the contained filter is correctly propagated in MF
File size: 15.8 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 01/2002 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2005
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MF
28//
29// With this filter you can filter in all variables from Mars parameter
30// containers.
31//
32// In the constructor you can give the filter rule, like
33// "MHillas.fLength < 15"
34// Where MHillas is the name of the parameter container in the parameter
35// list and fLength is the name of the data member which should be used
36// for the filter rule. If the name of the container is use specified
37// (MyHillas) the name to give would be:
38// "MyHillas.fLength < 15"
39//
40// Also more difficult rules are allowed, like:
41// "MHillas.fWidth<0.5 && MHillas.fLength<0.6"
42//
43// You can also use parantheses:
44// "MHillas.fSize>200 || (MHillas.fWidth<0.5 && MHillas.fLength<0.6)"
45//
46// If you want to use mathematic expressions (as defined in MDataChain)
47// you must encapsulate it in {}-parantheses, eg:
48// "{log10(MHillas.fSize)}>3"
49//
50// For more details on available functions and expressions see MDataChain!
51//
52// The allowed logigal conditionals are:
53// &&: logical and
54// ||: logical or
55// ^: exclusive or
56//
57// As conditional signs, for now, only:
58// <, >
59// are allowed.
60//
61// --------> '==' is NOT supported!
62//
63//
64// Warning: There is no priority rule build in. So better use parantheses
65// to get correct results. The rule is parsed/evaluated from the left
66// to the right, which means:
67//
68// "MHillas.fSize>200 || MHillas.fWidth<0.5 && MHillas.fLength<0.6"
69//
70// is parses as
71//
72// "(MHillas.fSize>200 || MHillas.fWidth<0.5) && MHillas.fLength<0.6"
73//
74//
75// FIXME: The possibility to use also complete filters is missing.
76// Maybe we can use gInterpreter->Calc("") for this.
77// gROOT->ProcessLineFast("line");
78//
79/////////////////////////////////////////////////////////////////////////////
80#include "MF.h"
81
82#include <ctype.h> // isalnum, ...
83#include <stdlib.h> // strtod, ...
84#include <fstream> // ofstream, ...
85
86#include <TMethodCall.h>
87
88#include "MParList.h"
89
90//#include "MFilterList.h"
91#include "MFDataPhrase.h"
92//#include "MFDataMember.h"
93
94#include "MLog.h"
95#include "MLogManip.h"
96
97ClassImp(MF);
98
99using namespace std;
100
101const TString MF::gsDefName = "MF";
102const TString MF::gsDefTitle = "Filter setup by a text-rule";
103
104// --------------------------------------------------------------------------
105//
106// Default Constructor. Don't use.
107//
108MF::MF() : fF(NULL), fAllowEmpty(kFALSE)
109{
110 fName = gsDefName.Data();
111 fTitle = gsDefTitle.Data();
112}
113
114// --------------------------------------------------------------------------
115//
116// Constructor. For the text describing the filter rule please see
117// the class description above.
118//
119MF::MF(const char *text, const char *name, const char *title) : fAllowEmpty(kFALSE)
120{
121 fName = name ? name : gsDefName.Data();
122 fTitle = title ? title : gsDefTitle.Data();
123
124 *fLog << inf << "Parsing filter rule... " << flush;
125 fF = new MFDataPhrase(text);
126 /*
127 if (!(fF=ParseString(text, 1)))
128 {
129 *fLog << err << dbginf << "Parsing '" << text << "' failed." << endl;
130 return;
131 }
132 */
133 *fLog << inf << "found: ";
134 fF->Print();
135 *fLog << endl;
136}
137
138// --------------------------------------------------------------------------
139//
140// Destructor. Delete filters.
141//
142MF::~MF()
143{
144 if (fF)
145 delete fF;
146}
147/*
148// --------------------------------------------------------------------------
149//
150// Returns the number of alphanumeric characters (including '.')
151// in the given string
152//
153Int_t MF::IsAlNum(TString txt) const
154{
155 int l = txt.Length();
156 for (int i = 0; i<l; i++)
157 if (!isalnum(txt[i]) && txt[i]!='.' && txt[i]!=';')
158 return i;
159
160 return l;
161}
162
163MFilter *MF::ParseRule(TString &txt, MFilter *filter0, Int_t level) const
164{
165 // For backward compatibility
166 txt.ReplaceAll("{", "(");
167 txt.ReplaceAll("}", ")");
168
169 const Int_t fg = txt.First('>');
170 const Int_t lg = txt.First('>');
171 const Int_t fl = txt.First('<');
172 const Int_t ll = txt.First('<');
173
174 if (fg<0 && fl<0)
175 {
176 MFilter *f = (MFilter*)GetNewObject(txt, MFilter::Class());
177 if (!f)
178 {
179 *fLog << err << dbginf << "Syntax Error: '" << txt << "' is neither a MFilter nor conditional sign found." << endl;
180 return NULL;
181 }
182 // FIXME: Search object through list...?
183 txt = "";
184 return f;
185 }
186
187 if (fg>=0 && fl>=0)
188 {
189 *fLog << err << dbginf << "Syntax Error: Two coditional signs found in " << txt << endl;
190 *fLog << "Currently you have to enclose all conditions in parantheses, like: \"(x<y) && (z<5)\"" << endl;
191 return NULL;
192 }
193 if (fg!=lg || fl!=ll)
194 {
195 *fLog << err << dbginf << "Syntax Error: Coditional sign found twice " << txt << endl;
196 return NULL;
197 }
198
199 const Int_t cond = fg<0 ? fl : fg;
200
201 const TString rule1 = txt(0, cond);
202 const TString rule2 = txt(cond+1, txt.Length());
203
204 Int_t lvl = gLog.GetDebugLevel();
205 gLog.SetDebugLevel(1);
206 MFilter *f = new MFDataChain(rule1.Data(), txt[cond], rule2.Data());
207 f->SetName(Form("Chain%02d%c", level, txt[cond]));
208 gLog.SetDebugLevel(lvl);
209
210 txt = "";
211 return f;
212}
213// --------------------------------------------------------------------------
214//
215// Parse a text string. Returns a corresponding filter of filter list.
216//
217MFilter *MF::ParseString(TString txt, Int_t level)
218{
219 MFilter *filter0=NULL;
220
221 TString type;
222 int nlist = 0;
223
224 while (!txt.IsNull())
225 {
226 MFilter *newfilter = NULL;
227
228 txt = txt.Strip(TString::kBoth);
229
230 //fLog << all << setw(level) << " " << "Text: " << level << " '" << txt << "'" << endl;
231
232 switch (txt[0])
233 {
234 case '(':
235 {
236 //
237 // Search for the corresponding parantheses
238 //
239 Int_t first=1;
240 for (int cnt=0; first<txt.Length(); first++)
241 {
242 if (txt[first]=='(')
243 cnt++;
244 if (txt[first]==')')
245 cnt--;
246
247 if (cnt==-1)
248 break;
249 }
250
251 if (first==txt.Length())
252 {
253 *fLog << err << dbginf << "Syntax Error: ')' missing." << endl;
254 if (filter0)
255 delete filter0;
256 return NULL;
257 }
258
259 //
260 // Make a copy of the 'interieur' and delete the substringä
261 // including the parantheses
262 //
263 TString sub = txt(1, first-1);
264 txt.Remove(0, first+1);
265
266 //
267 // Parse the substring
268 //
269 newfilter = ParseString(sub, level+1);
270 if (!newfilter)
271 {
272 *fLog << err << dbginf << "Parsing '" << sub << "' failed." << endl;
273 if (filter0)
274 delete filter0;
275 return NULL;
276 }
277 }
278 break;
279
280 case ')':
281 *fLog << err << dbginf << "Syntax Error: Too many ')'" << endl;
282 if (filter0)
283 delete filter0;
284 return NULL;
285
286
287 case '&':
288 case '|':
289 case '^':
290 if (filter0)
291 {
292 //
293 // Check for the type of the conditional
294 //
295 TString is = txt[0];
296 txt.Remove(0, 1);
297
298 if (is==txt[0] && is!='^')
299 {
300 is += txt[0];
301 txt.Remove(0, 1);
302 }
303
304 //
305 // If no filter is available or the available filter
306 // is of a different conditional we have to create a new
307 // filter list with the new conditional
308 //
309 if (!filter0->InheritsFrom(MFilterList::Class()) || type!=is)
310 {
311 MFilterList *list = new MFilterList(is);
312 list->SetName(Form("List_%s_%d", (const char*)is, 10*level+nlist++));
313
314 list->SetOwner();
315 list->AddToList(filter0);
316 filter0 = list;
317
318 type = is;
319 }
320 continue;
321 }
322
323 *fLog << err << dbginf << "Syntax Error: First argument of condition missing." << endl;
324 if (filter0)
325 delete filter0;
326 return NULL;
327
328 default:
329 newfilter = ParseRule(txt, filter0, level++);
330 if (!newfilter)
331 {
332 if (filter0)
333 delete filter0;
334 return NULL;
335 }
336 }
337
338 if (!filter0)
339 {
340 filter0 = newfilter;
341 continue;
342 }
343
344 if (!filter0->InheritsFrom(MFilterList::Class()))
345 continue;
346
347 ((MFilterList*)filter0)->AddToList(newfilter);
348 }
349
350 return filter0;
351}
352*/
353
354// --------------------------------------------------------------------------
355//
356// PreProcess all filters.
357//
358Int_t MF::PreProcess(MParList *plist)
359{
360 if (!fF)
361 {
362 if (IsAllowEmpty())
363 return kTRUE;
364
365 *fLog << err << dbginf << "No filter rule available." << endl;
366 return kFALSE;
367 }
368
369 fF->SetAccelerator(GetAccelerator());
370
371 if (!fF->CallPreProcess(plist))
372 {
373 *fLog << err << dbginf << "PreProcessing filters in ";
374 *fLog << fName << " failed." << endl;
375 return kFALSE;
376 }
377
378 return kTRUE;
379}
380
381// --------------------------------------------------------------------------
382//
383// Process all filters.
384//
385Int_t MF::Process()
386{
387 return fF ? fF->CallProcess() : kTRUE;
388}
389
390// --------------------------------------------------------------------------
391//
392// Postprocess all filters.
393//
394Int_t MF::PostProcess()
395{
396 return fF ? fF->CallPostProcess() : kTRUE;
397}
398
399// --------------------------------------------------------------------------
400//
401// Return the result of the filter rule. If no filter available the
402// condition is always true
403//
404Bool_t MF::IsExpressionTrue() const
405{
406 return fF ? fF->IsConditionTrue() : kTRUE;
407}
408
409// --------------------------------------------------------------------------
410//
411// Return a comma seperated list of all data members used in the chain.
412// This is mainly used in MTask::AddToBranchList
413//
414TString MF::GetDataMember() const
415{
416 return fF ? fF->GetDataMember() : (TString)"";
417}
418
419void MF::StreamPrimitive(ostream &out) const
420{
421 out << " MF " << GetUniqueName();
422
423 if (!fF)
424 {
425 out << ";" << endl;
426 return;
427 }
428
429 out << "(\"" << fF->GetRule() << "\"";
430 if (fName!=gsDefName || fTitle!=gsDefTitle)
431 {
432 out << "(\"" << fName << "\"";
433 if (fTitle!=gsDefTitle)
434 out << ", \"" << fTitle << "\"";
435 }
436 out << ");" << endl;
437
438}
439
440// --------------------------------------------------------------------------
441//
442// Print the rule.
443// if now filter is set up '<n/a>' is printed.
444//
445void MF::Print(Option_t *opt) const
446{
447 *fLog << all << underline << GetDescriptor() << endl;
448 if (fF)
449 fF->Print();
450 else
451 *fLog << "<n/a>";
452 *fLog << endl << endl;
453}
454
455// --------------------------------------------------------------------------
456//
457// Check for corresponding entries in resource file and setup filters.
458//
459// Assuming your MF-filter is called (Set/GetName): MyFilter
460//
461// First you can setup whether the filter is inverted or not:
462// MyFilter.Inverted: yes, no <default=no>
463//
464// Now setup the condition, eg:
465// MyFilter.Condition: MHillas.fSize<1000
466// or
467// MyFilter.Condition: MHillas.fSize>500 && MHillas.fSize<10000
468//
469// If you want to use more difficult filters you can split the
470// condition into subcondistions. Subcondistions are identified
471// by {}-brackets. Avoid trailing 0's! For example:
472//
473// MyFilter.Condition: MHillas.fSize>500 && {0} && {1}
474// MyFilter.0: MHillas.fSize>1000
475// MyFilter.1: MHillas.fSize<10000
476//
477// The numbering must be continous and start with 0. You can use
478// a subcondition more than once. All {}-brackets are simply replaced
479// by the correspodning conditions. The rules how conditions can
480// be written can be found in the class description of MF and MDataChain.
481//
482Int_t MF::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
483{
484
485 /*
486 // It is assumed that each filter itself calls MFilter::ReadEnv
487 // to process the Inverted directive
488 Bool_t rc = MFilter::ReadEnv(env, prefix, print);
489 if (rc==kERROR)
490 return kERROR;
491 */
492 if (IsEnvDefined(env, prefix, "Condition", print))
493 {
494 TString rule = GetEnvValue(env, prefix, "Condition", "");
495 rule.ReplaceAll(" ", "");
496
497 Int_t idx=0;
498 while (1)
499 {
500 TString cond;
501 if (IsEnvDefined(env, prefix, Form("%d", idx), print))
502 {
503 cond += "(";
504 cond += GetEnvValue(env, prefix, Form("%d", idx), "");
505 cond += ")";
506 }
507
508 if (cond.IsNull())
509 break;
510
511 rule.ReplaceAll(Form("{%d}", idx), cond);
512 idx++;
513 }
514
515 if (fF)
516 {
517 delete fF;
518 fF = 0;
519 }
520
521 if (rule.IsNull())
522 {
523 *fLog << warn << GetDescriptor() << " ::ReadEnv - WARNING: Empty condition found." << endl;
524 SetAllowEmpty();
525 return kTRUE;
526 }
527
528 SetAllowEmpty(kFALSE);
529
530 const Int_t fg = rule.First('>');
531 const Int_t fl = rule.First('<');
532 const Int_t fe = rule.First('=');
533
534 if (fg<0 && fl<0 && fe<0)
535 {
536 MFilter *f = (MFilter*)GetNewObject(rule, MFilter::Class());
537 if (!f)
538 {
539 *fLog << err << dbginf << "Syntax Error: '" << rule << "' is neither a MFilter nor conditional sign found." << endl;
540 return kERROR;
541 }
542 fF = f;
543 }
544 else
545 fF = new MFDataPhrase(rule);
546 /*
547 if (!(fF=ParseString(rule, 1)))
548 {
549 *fLog << err << "MF::ReadEnv - ERROR: Parsing '" << rule << "' failed." << endl;
550 return kERROR;
551 }
552 */
553 if (print)
554 {
555 *fLog << inf << "found: ";
556 fF->Print();
557 *fLog << endl;
558 }
559 }
560 else
561 {
562 if (!fF)
563 {
564 *fLog << warn << GetDescriptor() << " ::ReadEnv - WARNING: No condition found." << endl;
565 SetAllowEmpty();
566 return kFALSE;
567 }
568 }
569
570 // This is a workaround, because MFilter::ReadEnv and fF->ReadEnv
571 // might check for "Inverted" in this case both gets inverted
572 // and double-inversion is no inversion
573 /*
574 if (IsEnvDefined(env, prefix, "Inverted", print))
575 if (GetEnvValue(env, prefix, "Inverted", kFALSE)==kTRUE)
576 SetInverted(kFALSE);*/
577
578 return fF->ReadEnv(env, prefix, print);
579}
Note: See TracBrowser for help on using the repository browser.