source: trunk/MagicSoft/Mars/mfbase/MF.cc@ 7558

Last change on this file since 7558 was 7200, checked in by tbretz, 19 years ago
*** empty log message ***
File size: 15.1 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 "MFDataChain.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 if (!(fF=ParseString(text, 1)))
126 {
127 *fLog << err << dbginf << "Parsing '" << text << "' failed." << endl;
128 return;
129 }
130
131 *fLog << inf << "found: ";
132 fF->Print();
133 *fLog << endl;
134}
135
136// --------------------------------------------------------------------------
137//
138// Destructor. Delete filters.
139//
140MF::~MF()
141{
142 if (fF)
143 delete fF;
144}
145
146// --------------------------------------------------------------------------
147//
148// Returns the number of alphanumeric characters (including '.')
149// in the given string
150//
151Int_t MF::IsAlNum(TString txt) const
152{
153 int l = txt.Length();
154 for (int i = 0; i<l; i++)
155 if (!isalnum(txt[i]) && txt[i]!='.' && txt[i]!=';')
156 return i;
157
158 return l;
159}
160
161MFilter *MF::ParseRule(TString &txt, MFilter *filter0, Int_t level) const
162{
163 // For backward compatibility
164 txt.ReplaceAll("{", "(");
165 txt.ReplaceAll("}", ")");
166
167 const Int_t fg = txt.First('>');
168 const Int_t lg = txt.First('>');
169 const Int_t fl = txt.First('<');
170 const Int_t ll = txt.First('<');
171
172 if (fg<0 && fl<0)
173 {
174 MFilter *f = (MFilter*)GetNewObject(txt, MFilter::Class());
175 if (!f)
176 {
177 *fLog << err << dbginf << "Syntax Error: '" << txt << "' is neither a MFilter nor conditional sign found." << endl;
178 return NULL;
179 }
180 // FIXME: Search object through list...?
181 txt = "";
182 return f;
183 }
184
185 if (fg>=0 && fl>=0)
186 {
187 *fLog << err << dbginf << "Syntax Error: Two coditional signs found in " << txt << endl;
188 *fLog << "Currently you have to enclose all conditions in parantheses, like: \"(x<y) && (z<5)\"" << endl;
189 return NULL;
190 }
191 if (fg!=lg || fl!=ll)
192 {
193 *fLog << err << dbginf << "Syntax Error: Coditional sign found twice " << txt << endl;
194 return NULL;
195 }
196
197 const Int_t cond = fg<0 ? fl : fg;
198
199 const TString rule1 = txt(0, cond);
200 const TString rule2 = txt(cond+1, txt.Length());
201
202 Int_t lvl = gLog.GetDebugLevel();
203 gLog.SetDebugLevel(1);
204 MFilter *f = new MFDataChain(rule1.Data(), txt[cond], rule2.Data());
205 f->SetName(Form("Chain%02d%c", level, txt[cond]));
206 gLog.SetDebugLevel(lvl);
207
208 txt = "";
209 return f;
210}
211// --------------------------------------------------------------------------
212//
213// Parse a text string. Returns a corresponding filter of filter list.
214//
215MFilter *MF::ParseString(TString txt, Int_t level)
216{
217 MFilter *filter0=NULL;
218
219 TString type;
220 int nlist = 0;
221
222 while (!txt.IsNull())
223 {
224 MFilter *newfilter = NULL;
225
226 txt = txt.Strip(TString::kBoth);
227
228 //*fLog << all << setw(level) << " " << "Text: " << level << " '" << txt << "'" << endl;
229
230 switch (txt[0])
231 {
232 case '(':
233 {
234 //
235 // Search for the corresponding parantheses
236 //
237 Int_t first=1;
238 for (int cnt=0; first<txt.Length(); first++)
239 {
240 if (txt[first]=='(')
241 cnt++;
242 if (txt[first]==')')
243 cnt--;
244
245 if (cnt==-1)
246 break;
247 }
248
249 if (first==txt.Length())
250 {
251 *fLog << err << dbginf << "Syntax Error: ')' missing." << endl;
252 if (filter0)
253 delete filter0;
254 return NULL;
255 }
256
257 //
258 // Make a copy of the 'interieur' and delete the substringä
259 // including the parantheses
260 //
261 TString sub = txt(1, first-1);
262 txt.Remove(0, first+1);
263
264 //
265 // Parse the substring
266 //
267 newfilter = ParseString(sub, level+1);
268 if (!newfilter)
269 {
270 *fLog << err << dbginf << "Parsing '" << sub << "' failed." << endl;
271 if (filter0)
272 delete filter0;
273 return NULL;
274 }
275 }
276 break;
277
278 case ')':
279 *fLog << err << dbginf << "Syntax Error: Too many ')'" << endl;
280 if (filter0)
281 delete filter0;
282 return NULL;
283
284
285 case '&':
286 case '|':
287 case '^':
288 if (filter0)
289 {
290 //
291 // Check for the type of the conditional
292 //
293 TString is = txt[0];
294 txt.Remove(0, 1);
295
296 if (is==txt[0] && is!='^')
297 {
298 is += txt[0];
299 txt.Remove(0, 1);
300 }
301
302 //
303 // If no filter is available or the available filter
304 // is of a different conditional we have to create a new
305 // filter list with the new conditional
306 //
307 if (!filter0->InheritsFrom(MFilterList::Class()) || type!=is)
308 {
309 MFilterList *list = new MFilterList(is);
310 list->SetName(Form("List_%s_%d", (const char*)is, 10*level+nlist++));
311
312 list->SetOwner();
313 list->AddToList(filter0);
314 filter0 = list;
315
316 type = is;
317 }
318 continue;
319 }
320
321 *fLog << err << dbginf << "Syntax Error: First argument of condition missing." << endl;
322 if (filter0)
323 delete filter0;
324 return NULL;
325
326 default:
327 newfilter = ParseRule(txt, filter0, level++);
328 if (!newfilter)
329 {
330 if (filter0)
331 delete filter0;
332 return NULL;
333 }
334 }
335
336 if (!filter0)
337 {
338 filter0 = newfilter;
339 continue;
340 }
341
342 if (!filter0->InheritsFrom(MFilterList::Class()))
343 continue;
344
345 ((MFilterList*)filter0)->AddToList(newfilter);
346 }
347
348 return filter0;
349}
350
351// --------------------------------------------------------------------------
352//
353// PreProcess all filters.
354//
355Int_t MF::PreProcess(MParList *plist)
356{
357 if (!fF)
358 {
359 if (IsAllowEmpty())
360 return kTRUE;
361
362 *fLog << err << dbginf << "No filter rule available." << endl;
363 return kFALSE;
364 }
365
366 if (!fF->CallPreProcess(plist))
367 {
368 *fLog << err << dbginf << "PreProcessing filters in ";
369 *fLog << fName << " failed." << endl;
370 return kFALSE;
371 }
372
373 return kTRUE;
374}
375
376// --------------------------------------------------------------------------
377//
378// Process all filters.
379//
380Int_t MF::Process()
381{
382 return fF ? fF->CallProcess() : kTRUE;
383}
384
385// --------------------------------------------------------------------------
386//
387// Postprocess all filters.
388//
389Int_t MF::PostProcess()
390{
391 return fF ? fF->CallPostProcess() : kTRUE;
392}
393
394// --------------------------------------------------------------------------
395//
396// Return the result of the filter rule. If no filter available the
397// condition is always true
398//
399Bool_t MF::IsExpressionTrue() const
400{
401 return fF ? fF->IsConditionTrue() : kTRUE;
402}
403
404// --------------------------------------------------------------------------
405//
406// Return a comma seperated list of all data members used in the chain.
407// This is mainly used in MTask::AddToBranchList
408//
409TString MF::GetDataMember() const
410{
411 return fF ? fF->GetDataMember() : (TString)"";
412}
413
414void MF::StreamPrimitive(ofstream &out) const
415{
416 out << " MF " << GetUniqueName();
417
418 if (!fF)
419 {
420 out << ";" << endl;
421 return;
422 }
423
424 out << "(\"" << fF->GetRule() << "\"";
425 if (fName!=gsDefName || fTitle!=gsDefTitle)
426 {
427 out << "(\"" << fName << "\"";
428 if (fTitle!=gsDefTitle)
429 out << ", \"" << fTitle << "\"";
430 }
431 out << ");" << endl;
432
433}
434
435// --------------------------------------------------------------------------
436//
437// Print the rule.
438// if now filter is set up '<n/a>' is printed.
439//
440void MF::Print(Option_t *opt) const
441{
442 *fLog << all << underline << GetDescriptor() << endl;
443 if (fF)
444 fF->Print();
445 else
446 *fLog << "<n/a>";
447 *fLog << endl << endl;
448}
449
450// --------------------------------------------------------------------------
451//
452// Check for corresponding entries in resource file and setup filters.
453//
454// Assuming your MF-filter is called (Set/GetName): MyFilter
455//
456// First you can setup whether the filter is inverted or not:
457// MyFilter.Inverted: yes, no <default=no>
458//
459// Now setup the condition, eg:
460// MyFilter.Condition: MHillas.fSize<1000
461// or
462// MyFilter.Condition: MHillas.fSize>500 && MHillas.fSize<10000
463//
464// If you want to use more difficult filters you can split the
465// condition into subcondistions. Subcondistions are identified
466// by {}-brackets. Avoid trailing 0's! For example:
467//
468// MyFilter.Condition: MHillas.fSize>500 && {0} && {1}
469// MyFilter.0: MHillas.fSize>1000
470// MyFilter.1: MHillas.fSize<10000
471//
472// The numbering must be continous and start with 0. You can use
473// a subcondition more than once. All {}-brackets are simply replaced
474// by the correspodning conditions. The rules how conditions can
475// be written can be found in the class description of MF and MDataChain.
476//
477Int_t MF::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
478{
479
480 /*
481 Bool_t rc = MFilter::ReadEnv(env, prefix, print);
482 if (rc==kERROR)
483 return kERROR;
484 */
485 if (IsEnvDefined(env, prefix, "Condition", print))
486 {
487 TString rule = GetEnvValue(env, prefix, "Condition", "");
488 rule.ReplaceAll(" ", "");
489
490 Int_t idx=0;
491 while (1)
492 {
493 TString cond;
494 if (IsEnvDefined(env, prefix, Form("%d", idx), print))
495 {
496 cond += "(";
497 cond += GetEnvValue(env, prefix, Form("%d", idx), "");
498 cond += ")";
499 }
500
501 if (cond.IsNull())
502 break;
503
504 rule.ReplaceAll(Form("{%d}", idx), cond);
505 idx++;
506 }
507
508 if (fF)
509 {
510 delete fF;
511 fF = 0;
512 }
513
514 if (rule.IsNull())
515 {
516 *fLog << warn << GetDescriptor() << " ::ReadEnv - WARNING: Empty condition found." << endl;
517 SetAllowEmpty();
518 return kTRUE;
519 }
520
521 SetAllowEmpty(kFALSE);
522
523 if (!(fF=ParseString(rule, 1)))
524 {
525 *fLog << err << "MF::ReadEnv - ERROR: Parsing '" << rule << "' failed." << endl;
526 return kERROR;
527 }
528
529 if (print)
530 {
531 *fLog << inf << "found: ";
532 fF->Print();
533 *fLog << endl;
534 }
535 }
536 else
537 {
538 if (!fF)
539 {
540 *fLog << warn << GetDescriptor() << " ::ReadEnv - WARNING: No condition found." << endl;
541 SetAllowEmpty();
542 return kFALSE;
543 }
544 }
545
546 if (fF->ReadEnv(env, prefix, print)==kERROR)
547 return kERROR;
548
549 // This is a workaround, because MFilter::ReadEnv and fF->ReadEnv
550 // might check for "Inverted" in this case both gets inverted
551 // and double-inversion is no inversion
552 /*
553 if (IsEnvDefined(env, prefix, "Inverted", print))
554 if (GetEnvValue(env, prefix, "Inverted", kFALSE)==kTRUE)
555 SetInverted(kFALSE);*/
556
557 return kTRUE;
558}
Note: See TracBrowser for help on using the repository browser.