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

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