ce9a2771203275d308720c9e6eeb154d10702574
[u/mrichter/AliRoot.git] / STEER / AliSurveyObj.cxx
1 /**************************************************************************
2  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3  *                                                                        *
4  * Author: The ALICE Off-line Project.                                    *
5  * Contributors are mentioned in the code where appropriate.              *
6  *                                                                        *
7  * Permission to use, copy, modify and distribute this software and its   *
8  * documentation strictly for non-commercial purposes is hereby granted   *
9  * without fee, provided that the above copyright notice appears in all   *
10  * copies and that both the copyright notice and this permission notice   *
11  * appear in the supporting documentation. The authors make no claims     *
12  * about the suitability of this software for any purpose. It is          *
13  * provided "as is" without express or implied warranty.                  *
14  **************************************************************************/
15
16 /////////////////////////////////////////////////////////////////////
17 //                                                                 //
18 //  class AliSurveyObj                                             //
19 //  Retrieve and Convert survey data into ROOT Objects             //
20 //                                                                 //
21 /////////////////////////////////////////////////////////////////////
22
23 #include "AliSurveyObj.h"
24
25 //ROOT includes
26 #include "TObjArray.h"
27 #include "TGrid.h"
28 #include "TGridResult.h"
29 #include "TFile.h"
30 #include "TObjString.h"
31
32 //AliROOT includes
33 #include "AliLog.h"
34 #include "AliSurveyPoint.h"
35
36 ClassImp(AliSurveyObj)
37
38
39 const TString AliSurveyObj::fgkStorage = "alien://alice.cern.ch";  
40 const TString AliSurveyObj::fgkBaseFolder = "/alice/data/Reference";
41 const TString AliSurveyObj::fgkValidDetectors = "ACORDE,BABYFRAME,BACKFRAME,\
42 EMCAL,FMD,HMPID,ITS,L3 MAGNET,MUON,MUON ABSORBERS,MUON DIPOLE,PHOS,PMD,\
43 SPACEFRAME,SUPERSTRUCTURE,T0,TOF,TPC,TRD,VZERO,ZDC,GRP";
44 const TString AliSurveyObj::fgkGRPDetectors = "BABYFRAME,BACKFRAME,L3 MAGNET,\
45 SPACEFRAME,MUON DIPOLE,MUON ABSORBERS,GRP";
46 const TString AliSurveyObj::fgkMUONDetectors = "MUON,SUPERSTRUCTURE";
47
48   
49 //_____________________________________________________________________________
50 AliSurveyObj::AliSurveyObj():
51   TObject(),
52   fTitle(""),
53   fDate(""),
54   fDetector(""),
55   fURL("http://edms.cern.ch/"),
56   fReportNr(-1),
57   fVersion(-1),
58   fObs(""),
59   fCoordSys(""),
60   fUnits(""),
61   fNrColumns(-1),
62   fColNames(""),
63   fIsValid(kFALSE),
64   fGridUser(""),
65   fDataPoints(new TObjArray(1))
66 {
67   // constructor
68   fDataPoints->SetOwner(kTRUE);
69 }
70
71
72 //_____________________________________________________________________________
73 AliSurveyObj::~AliSurveyObj() {
74   //destructor
75   if (fDataPoints) {
76     fDataPoints->Delete();
77     fDataPoints = 0;
78   }
79 }
80
81
82 //_____________________________________________________________________________
83 AliSurveyObj::AliSurveyObj(const AliSurveyObj& surveyObj):
84   TObject(),
85   fTitle(surveyObj.fTitle),
86   fDate(surveyObj.fDate),
87   fDetector(surveyObj.fDetector),
88   fURL(surveyObj.fURL),
89   fReportNr(surveyObj.fReportNr),
90   fVersion(surveyObj.fVersion),
91   fObs(surveyObj.fObs),
92   fCoordSys(surveyObj.fCoordSys),
93   fUnits(surveyObj.fUnits),
94   fNrColumns(surveyObj.fNrColumns),
95   fColNames(surveyObj.fColNames),
96   fIsValid(surveyObj.fIsValid),
97   fGridUser(surveyObj.fGridUser),
98   fDataPoints(new TObjArray(1))
99 {
100   // copy constructor
101   TObject *curr = surveyObj.fDataPoints->First();
102   while (curr != 0) {
103     fDataPoints->Add(curr);
104     curr = surveyObj.fDataPoints->After(curr);
105   }
106 }
107
108
109 //_____________________________________________________________________________
110 AliSurveyObj& AliSurveyObj::operator=(const AliSurveyObj& surveyObj)
111 {
112   // assignment operator
113   if (this != &surveyObj) {
114     fTitle = surveyObj.fTitle;
115     fDate = surveyObj.fDate;
116     fDetector = surveyObj.fDetector;
117     fURL = surveyObj.fURL;
118     fReportNr = surveyObj.fReportNr;
119     fVersion = surveyObj.fVersion;
120     fObs = surveyObj.fObs;
121     fCoordSys = surveyObj.fCoordSys;
122     fUnits = surveyObj.fUnits;
123     fNrColumns = surveyObj.fNrColumns;
124     fColNames = surveyObj.fColNames;
125     fIsValid = surveyObj.fIsValid;
126     fGridUser = surveyObj.fGridUser;
127     TObject *curr = surveyObj.fDataPoints->First();
128     while (curr != 0) {
129       fDataPoints->Add(curr);
130       curr = surveyObj.fDataPoints->After(curr);
131     }
132   }
133   return *this;
134 }
135
136
137 //_____________________________________________________________________________
138 void AliSurveyObj::AddPoint(AliSurveyPoint* point) {
139   // Adds a point to the TObjArray which containst the list of points
140   fDataPoints->Add(point);
141   return;
142 }
143
144
145 //_____________________________________________________________________________
146 Bool_t AliSurveyObj::Connect(const char *gridUrl, const char *user) {
147   // Connects to the grid
148
149   // If the same "Grid" is alreay active, skip connection
150   if (!gGrid || gridUrl != gGrid->GridUrl() ||
151       (( strcmp(user,"") ) && ( strcmp(user,gGrid->GetUser()) )) ) {
152     // connection to the Grid
153     AliInfo("\nConnecting to the Grid...");
154     if (gGrid) {
155       AliInfo(Form("gGrid = %p; GridUrl = %s; gGrid->GridUrl() = %s", 
156                    gGrid, gridUrl, gGrid->GridUrl()));
157       AliInfo(Form("User = %s; gGrid->GetUser() = %s",
158                    user, gGrid->GetUser()));
159     }
160     TGrid::Connect(gridUrl,user);
161   }
162         
163   if(!gGrid) {
164     AliError("Connection failed!");
165     return kFALSE;
166   }
167   return kTRUE;
168 }
169
170
171 //_____________________________________________________________________________
172 Bool_t AliSurveyObj::OpenFile(TString openString) {
173   // Opens the file and reads it to a buffer
174
175   AliInfo(Form("Opening file \"%s\"\n for survey data", openString.Data()));
176
177   if (openString.BeginsWith("alien://"))
178     if (!Connect(fgkStorage.Data(), fGridUser.Data())) {
179       AliError(Form("Error connecting to GRID"));
180       return kFALSE;
181     }
182
183   TFile *file = TFile::Open(openString.Data(), "READ");
184   if ( !file ) {
185     AliError(Form("Error opening file \"%s\"", openString.Data()));
186     return kFALSE;
187   }
188
189   Int_t size = file->GetSize();
190
191   char *buf = new Char_t[size + 1];
192   memset(buf, '\0', size + 1);
193
194   file->Seek(0);  
195   if ( file->ReadBuffer(buf, size) ) {
196     AliError("Error reading file contents to buffer!");
197     return kFALSE;
198   }
199   AliInfo(Form("%l bytes read!\n", file->GetBytesRead()));
200   
201   ParseBuffer(buf);
202
203   file->Close();
204   delete[] buf;
205   return kTRUE;
206 }
207
208
209 //_____________________________________________________________________________
210 Bool_t AliSurveyObj::FillFromLocalFile(const Char_t* filename) {
211   // Fills the object from a file in a local filesystem
212
213   TString fullOpenString = "file://" + TString(filename) + "?filetype=raw";
214
215   return OpenFile(fullOpenString);
216 }
217
218
219 //_____________________________________________________________________________
220 Bool_t AliSurveyObj::IsValidDetector(TString detector) const {
221   // Checks if the detector name is valid
222
223   detector.ToUpper();
224
225   TObjArray *dets = fgkValidDetectors.Tokenize(',');
226   TObject *found = dets->FindObject(detector);
227   dets->Delete();
228   dets = 0;
229
230   if (!found) return kFALSE;
231   else return kTRUE;
232 }
233
234
235 //_____________________________________________________________________________
236 TString AliSurveyObj::RealFolderName(TString detector) const {
237   // Returns the actual folder name for a given detector 
238   // Some "detectors" don't have a folder of their own
239
240   detector.ToUpper();
241   TString folderName = detector;
242
243   TObjArray *dets = fgkGRPDetectors.Tokenize(',');
244   if (dets->FindObject(detector)) folderName = "GRP";
245   dets->Delete();
246   dets = 0;
247
248   dets = fgkMUONDetectors.Tokenize(',');
249   if (dets->FindObject(detector)) folderName = "MUON";
250   dets->Delete();  
251  
252
253
254   dets = 0;
255
256   return folderName;
257 }
258
259 //_____________________________________________________________________________
260 Bool_t AliSurveyObj::Fill(TString detector, Int_t reportNumber,
261                           TString username) {
262   // Fills the object from a file in the default storage location in AliEn.
263   // The highest version available is selected.
264
265   return Fill(detector, reportNumber, -1, username);
266 }
267
268 //_____________________________________________________________________________
269 Bool_t AliSurveyObj::Fill(TString detector, Int_t reportNumber,
270                           Int_t reportVersion, TString username) {
271   // Fills the object from a file in the default storage location in AliEn.
272   // A specific version is selected.
273
274   detector.ToUpper();
275   
276   // Check if <detector> is valid
277   if (!IsValidDetector(detector)) {
278     AliWarning(Form("Detector '%s' is not a valid detector/structure!", detector.Data()));
279     return kFALSE;
280   }
281
282   // Some "detectors" don't have a folder of their own
283   // TString detectorFolder = RealFolderName(detector);
284
285   // Check if <year>, <reportNumber> and <reportVersion> are valid (roughly)
286   if ((reportNumber < 1) || (reportVersion < -1) || (0 == reportVersion)) {
287     AliError("Invalid parameter values for AliSurveyObj::Fill. (Report Number or Report Version)");
288     return kFALSE;
289   }
290
291   // Check if the fGridUser is set, or specified
292   if (username.Length() > 0) SetGridUser(username);
293   else if (0 == fGridUser.Length()) {
294     AliError("GRID username not specified and not previously set!");
295     return kFALSE;
296   }
297
298   // Query AliEn for the available reports
299   TGridResult *res = QueryReports(detector, -1, reportNumber, reportVersion);
300   if (!res) AliError(Form("Error querying AliEn for detector '%s', \
301                            report number '%d' and report version '%d'.",
302                           detector.Data(), reportNumber, reportVersion));
303   Int_t numberEntries = res->GetEntries();
304   if (0 == numberEntries) {
305     AliError(Form("No report found for detector '%s', report number '%d' and report version '%d'",
306                   detector.Data(), reportNumber, reportVersion));
307     return kFALSE;
308   }
309
310   TString fileNamePath = "";
311   if (1 == numberEntries) fileNamePath = res->GetFileNamePath(0);
312   else if (numberEntries > 1) {
313     TString higherVerFNP = res->GetFileNamePath(0);
314     Int_t lastYear = FileNamePathToReportYear(higherVerFNP);
315     for (Int_t i = 1; i < numberEntries; ++i) {
316       TString currFNP = res->GetFileNamePath(i);
317       if (FileNamePathToReportVersion(currFNP) >
318           FileNamePathToReportVersion(higherVerFNP)) higherVerFNP = currFNP;
319       if (lastYear != FileNamePathToReportYear(currFNP))
320         AliWarning("Inconsistency detected, year differs for reports with the same report number! Please inform the responsible and check the report against the one in DCDB.");
321     }
322     fileNamePath = higherVerFNP;
323   }
324
325   TString fullOpenString = "alien://" + fileNamePath + "?filetype=raw";
326   /*
327   // Finally composes the full string
328   TString fullOpenString = "alien://" + fgkBaseFolder + "/" + detectorFolder + "/RawSurvey/";
329   fullOpenString += Form("%d/%d_v%d.txt?filetype=raw", year, reportNumber, reportVersion);
330   */
331
332   return OpenFile(fullOpenString);
333 }
334
335
336 //_____________________________________________________________________________
337 TString AliSurveyObj::FileNamePathToDetector(TString filename) const {
338   // Get the report number from the complete path in the format:
339   // /alice/data/Reference/HMPID/RawSurvey/2006/781282_v1.txt
340
341   TString ret = "";
342
343   if (filename.Length() > fgkBaseFolder.Length()) {
344     ret = filename.Remove(0, fgkBaseFolder.Length());
345     ret.Remove(TString::kLeading, '/');
346     ret = ret(0, ret.First('/'));
347     if (!IsValidDetector(ret)) ret = "";
348   } 
349   return ret;
350 }
351
352 ///alice/cern.ch/user/r/rsilva/TRD/RawSurvey/2007/.816582_v2.txt/v1.0
353
354 //_____________________________________________________________________________
355 Int_t AliSurveyObj::FileNamePathToReportYear(TString filename) const {
356   // Get the report year from the complete path in the format:
357   // /alice/data/Reference/HMPID/RawSurvey/2006/781282_v1.txt
358
359   TString ret = "";
360
361   if (filename.Length() > fgkBaseFolder.Length()) {
362     ret = filename.Remove(0, fgkBaseFolder.Length());
363     ret.Remove(TString::kLeading, '/');
364     Int_t beg = ret.First('/') + TString("RawSurvey/").Length() + 1;
365     ret = ret(beg, ret.Last('/') - beg);
366     return ret.Atoi();
367   } 
368   return -1;
369 }
370
371
372 //_____________________________________________________________________________
373 Int_t AliSurveyObj::FileNamePathToReportNumber(TString filename) const {
374   // Get the report number from the complete path in the format:
375   // /alice/data/Reference/HMPID/RawSurvey/2006/781282_v1.txt
376
377   TString ret = "";
378
379   if (filename.Length() > fgkBaseFolder.Length()) {
380     ret = filename.Remove(0, fgkBaseFolder.Length());
381     ret.Remove(TString::kLeading, '/');
382     if ((ret.CountChar('/') > 3) || (ret.CountChar('.') > 1)) {
383       AliWarning("Error getting the Report Number from the filename path!");
384       return -1;
385     }
386     ret = ret(ret.Last('/') + 1 , ret.Last('_') - ret.Last('/') - 1);
387     return ret.Atoi();
388   } 
389   AliWarning("Error getting the Report Number from the filename path!");
390   return -1;
391 }
392
393
394 //_____________________________________________________________________________
395 Int_t AliSurveyObj::FileNamePathToReportVersion(TString filename) const {
396   // Get the report version from the complete path in the format:
397   // /alice/data/Reference/HMPID/RawSurvey/2006/781282_v1.txt
398
399   TString ret = "";
400
401   if (filename.Length() > fgkBaseFolder.Length()) {
402     ret = filename.Remove(0, fgkBaseFolder.Length());
403     ret.Remove(TString::kLeading, '/');
404     if ((ret.CountChar('/') > 3) || (ret.CountChar('.') > 1)) {
405       AliWarning("Error getting the Report Version from the filename path!");
406       return -1;
407     }
408     ret = ret(ret.Last('_') + 1 + 1 , ret.Last('.') - ret.Last('_') - 1 - 1);
409     return ret.Atoi();
410   } 
411   AliWarning("Error getting the Report Version from the filename path!");
412   return -1;
413 }
414
415
416 //_____________________________________________________________________________
417 void AliSurveyObj::ListValidDetectors() {
418     // List the valid detector names
419     Printf("Listing all valid detectors:\n");
420     TObjArray *dets = fgkValidDetectors.Tokenize(',');
421     for (int i = 0; i < dets->GetEntries(); ++i) 
422         Printf("%s", ((TObjString *) dets->At(i))->GetString().Data());
423     dets->Delete();
424     dets = 0;
425     Printf("Some reports are stored in more general folders.\n"
426             "These reports can be opened using either name, the original or the\n"
427             "folder name. Example: 'SPACEFRAME' or 'GRP' are both valid when\n"
428             "opening a report for the Spaceframe.\n\n"
429             "Detectors stored in 'MUON' folder:");
430     dets = fgkMUONDetectors.Tokenize(',');
431     for (int i = 0; i < dets->GetEntries(); ++i) 
432         Printf("%s", ((TObjString *) dets->At(i))->GetString().Data());
433     dets->Delete();
434     dets = 0;
435     Printf("Detectors stored in 'GRP' folder:");
436     dets = fgkGRPDetectors.Tokenize(',');
437     for (int i = 0; i < dets->GetEntries(); ++i) 
438         Printf("%s", ((TObjString *) dets->At(i))->GetString().Data());
439     dets->Delete();
440     dets = 0;
441     return;
442 }
443
444
445 //_____________________________________________________________________________
446 TGridResult * AliSurveyObj::QueryReports(TString detector, Int_t year,
447                                          Int_t reportNumber,
448                                          Int_t reportVersion) {
449   // Queries AliEn for existing reports matching the specified conditions
450   TString lsArg = fgkBaseFolder;
451   
452   TString detectorFolder = "";
453   if (detector.Length() > 0) {
454     detector.ToUpper();
455     // Check if <detector> is valid
456     if (!IsValidDetector(detector)) {
457       AliError(Form("Detector '%s' is not a valid detector/structure!",
458                     detector.Data()));
459       return 0;
460     }
461     // Some "detectors" don't have a folder of their own
462     detectorFolder = "/" + RealFolderName(detector);
463   } else detectorFolder = "/*";
464
465   lsArg += detectorFolder + "/RawSurvey";
466
467   TString yearFolder = "";
468   if (year > 1950) yearFolder.Form("/%d", year);
469   else yearFolder = "/*";
470
471   TString reportFolder = "";
472   if (reportNumber > 0) reportFolder.Form("/%d", reportNumber);
473   else reportFolder = "/*";
474
475   TString versionFolder = "";
476   if (reportVersion > 0) versionFolder.Form("_v%d", reportVersion);
477   else versionFolder = "_v*";
478
479   lsArg += yearFolder + reportFolder + versionFolder + ".txt";
480
481   AliInfo(Form("\nLooking for:  %s \n", lsArg.Data()));
482   
483   // Check if fGridUser is set and Connect to AliEn
484   if (0 == fGridUser.Length()) {
485     AliError("To use this method it's necessary to call SetGridUser(...) in advance.");
486     return 0;
487   } else if (!Connect(fgkStorage.Data(), fGridUser.Data())) {
488     AliError(Form("Error connecting to GRID"));
489     return 0;
490   }
491   return gGrid->Ls(lsArg);
492 }
493
494
495 //_____________________________________________________________________________
496 Int_t AliSurveyObj::ListReports(TString detector, Int_t year, 
497                                 Int_t reportNumber,
498                                 Int_t reportVersion) {
499   // Lists all available reports matching the specified conditions
500   // Returns the number of reports found
501
502   TGridResult *res = QueryReports(detector, year, reportNumber, reportVersion);
503   
504   if (0 == res) {
505     AliError("Query failed.");
506     return 0;
507   }
508
509   TString fn = "";
510   Int_t numberEntries = res->GetEntries();
511
512   if (numberEntries > 0) {
513     Printf(Form("%d reports found:", numberEntries));
514     for (int i = 0; i < res->GetEntries(); ++i) {
515       fn = res->GetFileNamePath(i);
516       Printf(Form("Detector:%s\tYear:%d\tEDMS Report Number:%d\tVersion:%d",
517                   FileNamePathToDetector(fn).Data(),
518                   FileNamePathToReportYear(fn),
519                   FileNamePathToReportNumber(fn),
520                   FileNamePathToReportVersion(fn)));
521     }
522     delete res;
523     return numberEntries;
524   } else {
525     AliInfo("No results found for the requested query.");
526     delete res;
527     return 0;
528   }
529 }
530
531
532 //_____________________________________________________________________________
533 void AliSurveyObj::SetGridUser(TString username){
534   // Set the username used to connect to the GRID
535   fGridUser = username;
536   return;
537 }
538
539
540 //_____________________________________________________________________________
541 TString &AliSurveyObj::Sanitize(TString str) {
542   // Cleans up a line of new line and carriage return characters.
543   // (Specially usefull for files created in Windows.)
544
545   str.Remove(TString::kTrailing, '\r');
546   str.Remove(TString::kTrailing, '\n');
547   str.Remove(TString::kTrailing, '\r');
548
549   if (!str.IsAscii()) {
550     AliWarning("Warning: Non-ASCII characters!\n");
551     str = "";
552   }
553   return str.Remove(TString::kBoth, ' ');
554 }
555
556
557 //_____________________________________________________________________________
558 Bool_t AliSurveyObj::ParseBuffer(const Char_t* buf) {
559   // Parses a character buffer assuming the format defined with the TS/SU
560   // http://aliceinfo/Offline/Activities/Alignment/SurveyInformation.html
561
562   // If the object is already filled clean it up
563   if (fIsValid) Reset();
564
565   // Copy the buffer to a TString to use Tokenize
566   TString buffer = TString(buf);
567   TObjArray *linesRaw = buffer.Tokenize('\n');
568   // replace the array of lines with an array of sanitized lines
569   // in the process we remove empty lines, particularly disturbing
570   // in case of dos fileformat
571   TString oneLine = "";
572   TObjString* oneLineObj = 0;
573   TObjArray *lines = new TObjArray();
574   for(Int_t i=0; i<linesRaw->GetEntries(); i++)
575   {
576           oneLine = ((TObjString *)(linesRaw->At(i)))->GetString().Data();
577           oneLine = Sanitize(oneLine);
578           if (oneLine.Length() == 0) continue;
579           oneLineObj = new TObjString(oneLine);
580           lines->Add(oneLineObj);
581   }
582
583   linesRaw->Delete();
584   delete linesRaw;
585   linesRaw = NULL;
586
587   TObjArray *dataLine = NULL; // Used to Tokenize each point/line read
588   TObjArray *colLine = NULL; // Used to Tokenize the column names
589
590   // Some local variables declarations and initializations
591   const Int_t kFieldCheck = 10;
592   Bool_t check[kFieldCheck]; // used to check that mandatory column names are not missing
593   for (Int_t i = 0; i < kFieldCheck; ++i) check[i] = kFALSE;
594   TString tmpname = "";
595   Float_t tmpx = 0.0, tmpy = 0.0, tmpz = 0.0;
596   Float_t tmpprecX = -1., tmpprecY = -1., tmpprecZ = -1.;
597   Char_t tmptype = '\0';
598   Bool_t tmptarg = kTRUE;
599   AliSurveyPoint *dp = 0;
600   TString *orderedValues[9] = {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0};
601   TString value[9];
602
603   Int_t nrLines = lines->GetEntries();
604   Printf("Lines in file: %d\n", nrLines); 
605
606   // The main cycle, the buffer is parsed a line at a time
607   TString currLine = "", nextLine = "";
608   for (Int_t i = 0; i < nrLines; ++i) {
609
610     // Get the next line
611     currLine = ((TObjString *)(lines->At(i)))->GetString().Data();
612     nextLine = ((i + 1) < nrLines ? ((TObjString *)(lines->At(i + 1)))->GetString().Data() : "");
613     // Printf("%d: \"%s\"", i, currLine.Data());
614     // Printf("%d:  \"%s\"\n", i+1, nextLine.Data());
615     
616     // The line contains a keyword
617     if (currLine.BeginsWith(">") && !nextLine.BeginsWith(">")) {
618       currLine.Remove(TString::kLeading, '>');
619       currLine.Remove(TString::kTrailing, ':');
620       currLine.Remove(TString::kBoth, ' ');
621       nextLine.Remove(TString::kBoth, ' ');
622       AliDebug(2, Form(" -> field line: \"%s\"\n", currLine.Data()));
623       AliDebug(2, Form(" -> value line: \"%s\"\n", nextLine.Data()));
624       
625       if (currLine.BeginsWith("Title", TString::kIgnoreCase)) {
626         // Report Title
627         fTitle = nextLine;
628         ++i;
629       } else if (currLine.BeginsWith("Date", TString::kIgnoreCase)) {
630         // Report(measurement) Date
631         fDate = nextLine;
632         ++i;
633       } else if (currLine.BeginsWith("Subdetector", TString::kIgnoreCase)) {
634         // Subdetector or structure
635         fDetector = nextLine;
636         ++i;
637       } else if (currLine.BeginsWith("Report URL", TString::kIgnoreCase)) {
638         // Report URL in EDMS
639         if (nextLine.BeginsWith("http://edms.cern.ch/document/", TString::kIgnoreCase) ||
640             nextLine.BeginsWith("https://edms.cern.ch/document/", TString::kIgnoreCase)) {
641           fURL = nextLine;
642           nextLine.Remove(TString::kTrailing, '/');
643           nextLine = nextLine(nextLine.Last('/') + 1, nextLine.Length() - nextLine.Last('/') + 1);
644           
645           Int_t sscanftmp = 0;
646           if (1 != sscanf(nextLine.Data(), "%d", &sscanftmp)) {
647             AliError("Survey text file sintax error! (incorrectly formatted Report URL)");
648             lines->Delete();
649             delete lines; lines = NULL;
650             return kFALSE;
651           }
652           fReportNr = nextLine.Atoi();
653           //Printf(" $$ %d $$\n", fReportNr);
654           ++i;
655         } else { 
656           // URL incorrectly formatted
657           AliError("Survey text file sintax error! (incorrectly formatted Report URL)");
658           return kFALSE;
659         }
660       } else if (currLine.BeginsWith("Version", TString::kIgnoreCase)) {
661         // Report version
662         if (!nextLine.IsDigit()) {
663           lines->Delete();
664           delete lines; lines = NULL;
665           AliError("Survey text file sintax error! (incorrectly formatted Report Version)");
666           return kFALSE;
667         }
668         fVersion = nextLine.Atoi();
669         ++i;
670       } else if (currLine.BeginsWith("General Observations", TString::kIgnoreCase)) {
671         // Observations
672         fObs = "";
673         // Can be more than 1 line. Loop until another keyword is found
674         while (('>' != nextLine[0]) && (nextLine.Length() > 0) && (i < nrLines)) {      
675           fObs += (0 == fObs.Length()) ? nextLine : " / " + nextLine;
676           ++i;
677           nextLine = ((i + 1) < nrLines ? ((TObjString *)(lines->At(i + 1)))->GetString().Data() : "");
678         }
679       } else if (currLine.BeginsWith("Coordinate System", TString::kIgnoreCase)) {
680         // Coordinate System
681         fCoordSys = nextLine;
682         ++i;
683       } else if (currLine.BeginsWith("Units", TString::kIgnoreCase)) {
684         // Measurement Unit
685         fUnits = nextLine;
686         ++i;
687       } else if (currLine.BeginsWith("Nr Columns", TString::kIgnoreCase)) {
688         // Number of columns in the "Data" section
689         if (!nextLine.IsDigit()) {
690           lines->Delete();
691           delete lines; lines = NULL;
692           AliError("Survey text file sintax error! (incorrectly formatted Number of Columns)");
693           return kFALSE;
694         }
695         fNrColumns = nextLine.Atoi();
696         ++i;
697       } else if (currLine.BeginsWith("Column Names", TString::kIgnoreCase)) {
698         // Column names separated by commas
699         fColNames = nextLine;
700         colLine = nextLine.Tokenize(',');
701         if (colLine->GetEntries() != fNrColumns) {
702           AliError("Survey text file sintax error! (Declared number of Columns doesn't match number of column names)");
703           colLine->Delete();
704           lines->Delete();
705           delete lines; lines = NULL;
706           return kFALSE;
707         }
708         ++i;
709         
710         // booleans to track if precision for 1 axis is defined in more than one column
711         Bool_t prX = kFALSE;
712         Bool_t prY = kFALSE;
713         Bool_t prZ = kFALSE;
714         
715         for (Int_t j = 0; j < fNrColumns; ++j) {
716           TString cn = ((TObjString *)(colLine->At(j)))->GetString();
717           if (cn.BeginsWith("Point Name", TString::kIgnoreCase)) {
718             orderedValues[0] = &value[j];
719             check[0] = kTRUE;
720           } else if (cn.BeginsWith("X", TString::kIgnoreCase)) {
721             orderedValues[1] = &value[j];
722             check[1] = kTRUE;
723           } else if (cn.BeginsWith("Y", TString::kIgnoreCase)) {
724             orderedValues[2] = &value[j];
725             check[2] = kTRUE;
726           } else if (cn.BeginsWith("Z", TString::kIgnoreCase)) {
727             orderedValues[3] = &value[j];
728             check[3] = kTRUE;
729           } else if (cn.BeginsWith("Precision", TString::kIgnoreCase)) {
730             TString tmpCN = cn(0, cn.First('('));
731             Int_t precLength = TString("Precision").Length();
732             if (precLength == tmpCN.Length()) {
733               if(!orderedValues[6]){
734                 orderedValues[6] = &value[j];
735                 check[6] = kTRUE;
736               }else{
737                 AliWarning("Global precision will not be used for X axis");
738               }
739               if(!orderedValues[7]){
740                 orderedValues[7] = &value[j];
741                 check[7] = kTRUE;
742               }else{
743                 AliWarning("Global precision will not be used for Y axis");
744               }
745               if(!orderedValues[8]){
746                 orderedValues[8] = &value[j];
747                 check[8] = kTRUE;
748               }else{
749                 AliWarning("Global precision will not be used for Z axis");
750               }
751             } else {
752               Bool_t orXYZ = kFALSE;
753               TString axis = cn(precLength, tmpCN.Length() - precLength);
754               if (axis.Contains('X', TString::kIgnoreCase)) {
755                 if(!prX){
756                   orderedValues[6] = &value[j];
757                   check[6] = kTRUE;
758                   orXYZ = kTRUE;
759                   prX = kTRUE;
760                 }else{
761                   AliError("Precision for X axis was already set!");
762                   return kFALSE;
763                 }
764               }
765               if (axis.Contains('Y', TString::kIgnoreCase)) {
766                 if(!prY){
767                   orderedValues[7] = &value[j];
768                   check[7] = kTRUE;
769                   orXYZ = kTRUE;
770                   prY = kTRUE;
771                 }else{
772                   AliError("Precision for Y axis was already set!");
773                   return kFALSE;
774                 }
775               }
776               if (axis.Contains('Z', TString::kIgnoreCase)) {
777                 if(!prZ){
778                   orderedValues[8] = &value[j];
779                   check[8] = kTRUE;
780                   orXYZ = kTRUE;
781                   prZ = kTRUE;
782                 }else{
783                   AliError("Precision for Z axis was already set!");
784                   return kFALSE;
785                 }
786               }
787               if(!orXYZ)
788               {
789                 AliError("Survey text file sintax error: precision column name does not refer to any axis!");
790                 return kFALSE;
791               }
792             }
793           } else if (cn.BeginsWith("Point Type", TString::kIgnoreCase)) {
794             orderedValues[4] = &value[j];
795             check[4] = kTRUE;
796           } else if (cn.BeginsWith("Target Used", TString::kIgnoreCase)) {
797             orderedValues[5] = &value[j];
798             check[5] = kTRUE;
799           }
800         }
801
802         // Check if all the mandatory fields exist in the line with column names
803         if(!check[0]){
804           AliError("Missing mandatory column \"Point Name\"!");
805           return kFALSE;
806         }
807         if(!(check[1]&&check[2]&&check[3])){
808           AliError("Missing one or more mandatory columns for coordinates \"X\",\"Y\",\"Z\"");
809           return kFALSE;
810         }
811         if(!check[4]){
812           AliError("Missing mandatory column \"Point Type\"!");
813           return kFALSE;
814         }
815         if(!(check[6]&&check[7]&&check[8])){
816           AliError("Missing one or more mandatory columns for precision along \"X\",\"Y\",\"Z\" axes");
817           return kFALSE;
818         }
819
820       } else if (currLine.BeginsWith("Data", TString::kIgnoreCase)) {
821         // Data section!
822         while ((nextLine.Length() > 0) && ('>' != nextLine[0])) {
823
824           // Printf("Data LINE: \"%s\": %d\n", nextLine.Data(), nextLine.Length());
825
826           // What is the separator used between fields?
827           // The allowed are: comma (','), tab ('\t'), and space (' ')
828           if (fNrColumns == nextLine.CountChar(',') + 1) dataLine = nextLine.Tokenize(',');
829           else if (fNrColumns == nextLine.CountChar('\t') + 1) dataLine = nextLine.Tokenize('\t');
830           else if (fNrColumns == nextLine.CountChar(' ') + 1) dataLine = nextLine.Tokenize(' ');
831           else {
832             // Error (No separator was found!)
833             AliError("Survey text file syntax error! Error processing data line!");
834             lines->Delete();
835             delete lines; lines = NULL;
836             return kFALSE;
837           }
838
839           if (dataLine->GetEntries() != fNrColumns) {
840             // The number of columns doesn't match the number specified in the header
841             AliError("Survey text file sintax error! (Number of entries in line is different from number of Columns)");
842             dataLine->Delete();
843             lines->Delete();
844             delete lines; lines = NULL;
845             return kFALSE;
846           }
847
848           // Process the data line using the column names as index
849           for (Int_t j = 0; j < dataLine->GetEntries(); ++j) {
850             value[j] = ((TObjString *)(dataLine->At(j)))->GetString();
851           }
852           tmpname = *orderedValues[0];
853           tmpx = orderedValues[1]->Atof();
854           tmpy = orderedValues[2]->Atof();
855           tmpz = orderedValues[3]->Atof();
856           tmpprecX = orderedValues[6]->Atof();
857           tmpprecY = orderedValues[7]->Atof();
858           tmpprecZ = orderedValues[8]->Atof();
859           tmptype = orderedValues[4]->Data()[0];
860           if(orderedValues[5]) tmptarg = (orderedValues[5]->Data()[0] == 'Y') ? kTRUE : kFALSE;
861
862           dp = new AliSurveyPoint(tmpname, tmpx, tmpy, tmpz, tmpprecX, tmpprecY, tmpprecZ, tmptype, tmptarg);
863           if(AliLog::GetDebugLevel("","AliSurveyObj")>1) dp->PrintPoint();
864           AddPoint(dp);
865
866           dataLine->Delete();
867           dataLine = NULL;
868           ++i;
869           nextLine = ((i + 1) < nrLines ? ((TObjString *)(lines->At(i + 1)))->GetString().Data() : "");
870         }
871       }
872     } else {
873       AliError("Survey text file sintax error!");
874       lines->Delete();
875       delete lines; lines = NULL;
876       return kFALSE;
877     }
878   }
879   lines->Delete();
880   delete lines; lines = NULL;
881   fIsValid = kTRUE;
882   return kTRUE;
883 }
884
885 //_____________________________________________________________________________
886 void AliSurveyObj::Reset() {
887   // Resets the AliSurveyObj to a clean object.
888   // Used if the same object is filled more than once
889   
890   if (fDataPoints) {
891     fDataPoints->Delete();
892     fDataPoints = 0;
893   }
894   fTitle = "";
895   fDate = "";
896   fDetector = "";
897   fURL = "http://edms.cern.ch/";
898   fReportNr = -1;
899   fVersion = -1;
900   fObs = "";
901   fCoordSys = "";
902   fUnits = "";
903   fNrColumns = -1;
904   fColNames = "";
905   fIsValid = kFALSE;
906   fDataPoints = new TObjArray(1);
907   fDataPoints->SetOwner(kTRUE);
908   return;
909 }
910