Logo ROOT   6.10/09
Reference Guide
TBranchBrowsable.cxx
Go to the documentation of this file.
1 // @(#)root/tree:$Id: c5d87ada87b506444a9d0bc027d622273440b891 $
2 // Author: Axel Naumann 14/10/2004
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TBranchBrowsable.h"
13 #include "TBranchElement.h"
14 #include "TBranchObject.h"
15 #include "TMethod.h"
16 #include "TBrowser.h"
17 #include "TTree.h"
18 #include "TLeafObject.h"
19 #include "TClonesArray.h"
20 #include "TVirtualPad.h"
21 #include "TClass.h"
22 #include "TBaseClass.h"
23 #include "TDataMember.h"
24 #include "TStreamerInfo.h"
25 #include "TStreamerElement.h"
27 #include "TRef.h"
28 #include <algorithm>
29 
31 
32 /** \class TVirtualBranchBrowsable
33 \ingroup tree
34 
35 TVirtualBranchBrowsable is a base class (not really abstract, but useless
36 by itself) for helper objects that extend TBranch's browsing support.
37 Each registered derived class's generator method is called, which fills
38 all created helper objects into a list which can then be browsed.
39 For details of what these browser helper objects can do, see e.g.
40 TMethodBrowsable, which allows methods to show up in the TBrowser.
41 
42 Only registered helper objects are created. By default, only
43 TMethodBrowsable, TNonSplitBrowsable, and TCollectionPropertyBrowsable
44 are registered (see RegisterDefaultGenerators). You can prevent any of
45 their objects to show up in the browser by unregistering the generator:
46 ~~~ {.cpp}
47  TMethodBrowsable::Unregister()
48 ~~~
49 will stop creating browsable method helper objects from that call on.
50 Note that these helper objects are cached (in TBranch::fBrowsables);
51 already created (and thus cached) browsables will still appear in the
52 browser even after unregistering the corresponding generator.
53 
54 You can implement your own browsable objects and thier generator; see
55 e.g. the simple TCollectionPropertyBrowsable. Note that you will have
56 to register your generator just like any other, and that you should
57 implement the following methods for your own class, mainly for
58 consistency reasons:
59 ~~~ {.cpp}
60  static void Register() {
61  TVirtualBranchBrowsable::RegisterGenerator(GetBrowsables); }
62  static void Unregister() {
63  TVirtualBranchBrowsable::UnregisterGenerator(GetBrowsables); }
64 ~~~
65 where GetBrowsables is a static member function of your class, that
66 creates the browsable helper objects, and has the signature
67 ~~~ {.cpp}
68  static Int_t GetBrowsables(TList& list, const TBranch* branch,
69  const TVirtualBranchBrowsable* parent=0);
70 ~~~
71 
72 It has to return the number of browsable helper objects for parent
73 (or, if NULL, for branch) which are added to the list.
74 */
75 
76 std::list<TVirtualBranchBrowsable::MethodCreateListOfBrowsables_t>
79 
80 ////////////////////////////////////////////////////////////////////////////////
81 /// Constructor setting all members according to parameters.
82 
84  Bool_t typeIsPointer,
85  const TVirtualBranchBrowsable* parent /*=0*/):
86 fBranch(branch), fParent(parent), fLeaves(0), fClass(type), fTypeIsPointer(typeIsPointer)
87 {
89  if (!branch)
90  Warning("TVirtualBranchBrowsable", "branch is NULL!");
91 }
92 
93 ////////////////////////////////////////////////////////////////////////////////
94 /// Destructor. Delete our leaves.
95 
97 {
98  delete fLeaves;
99 }
100 
101 ////////////////////////////////////////////////////////////////////////////////
102 /// Calls TTree::Draw on the method if return type is not a class;
103 /// otherwise expands returned object's "folder"
104 
106 {
107  if (!fClass
109  && fClass->GetCollectionProxy()->GetType() > 0)) {
110  TString name;
111  GetScope(name);
112 
113  // If this is meant to be run on the collection
114  // we need to "move" the "@" from branch.@member
115  // to branch@.member
116  name.ReplaceAll(".@","@.");
117  name.ReplaceAll("->@","@->");
118 
119  TTree* tree=0;
120  if (!fBranch) {
121  Error("Browse", "branch not set - might access wrong tree!");
122  return;
123  } else tree=fBranch->GetTree();
124  tree->Draw(name, "", b ? b->GetDrawOption() : "");
125  if (gPad) gPad->Update();
126  } else
127  if (GetLeaves()) GetLeaves()->Browse(b);
128 }
129 
130 ////////////////////////////////////////////////////////////////////////////////
131 /// Askes all registered generators to fill their browsables into
132 /// the list. The browsables are generated for a given parent,
133 /// or (if 0), for a given branch. The branch is passed down to
134 /// leaves of TVirtualBranchBrowsable, too, as we need to access
135 /// the branch's TTree to be able to traw.
136 
138  const TVirtualBranchBrowsable* parent /* =0 */)
139 {
141  std::list<MethodCreateListOfBrowsables_t>::iterator iGenerator;
142  Int_t numCreated=0;
143  for (iGenerator=fgGenerators.begin(); iGenerator!=fgGenerators.end(); iGenerator++)
144  numCreated+=(*(*iGenerator))(li, branch, parent);
145  return numCreated;
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// Check whether the branch (or the parent) contains a collection.
150 /// If it does, set "contained" to the contained type (if we can
151 /// retrieve it) and return the TClass for the collection. Set
152 /// "contained" to the branch's (or parent's) contained object's
153 /// class for non-collections, returning 0.
154 ///
155 /// Only one of "branch" or "parent" can ge given (depending on whether
156 /// we are creating browsable objects for a branch or for another
157 /// browsable object)
158 
160  const TVirtualBranchBrowsable* parent,
161  TClass* &contained)
162 {
163  contained=0;
164  TClass* type=0;
165  if (parent)
166  type=parent->GetClassType();
167  else if (branch) {
168  if (branch->IsA()==TBranchElement::Class()) {
169  // could be a split TClonesArray
170  TBranchElement* be=(TBranchElement*) branch;
171 
172  // this is the contained type - if !=0
173  const char* clonesname=be->GetClonesName();
174  if (clonesname && strlen(clonesname))
175  contained=TClass::GetClass(clonesname);
176 
177  // check if we're in a sub-branch of this class
178  // we can only find out asking the streamer given our ID
179  TStreamerElement *element=0;
180  if (be->GetID()>=0 && be->GetInfo()
181  && (be->GetID() < be->GetInfo()->GetNelement())
182  && be->GetInfo()->IsCompiled()
183  && (element=be->GetInfo()->GetElement(be->GetID()))) {
184  // if contained is set (i.e. GetClonesName was successful),
185  // this element containes the container, otherwise it's the
186  // contained
187  if (contained)
188  // we have all we need
189  return element->GetClassPointer();
190  else
191  type=element->GetClassPointer();
192  } else if (clonesname && strlen(clonesname)) {
193  // we have a clones name, and the TCA is not split:
194  contained=TClass::GetClass(clonesname);
195  return TClass::GetClass(be->GetClassName());
196  } else
197  type=TClass::GetClass(be->GetClassName());
198  } else if (branch->IsA()==TBranchObject::Class()) {
199  // could be an unsplit TClonesArray
200  TBranchObject* bo=(TBranchObject*)branch;
201  const char* clonesname=bo->GetClassName();
202  contained=0;
203  if (!clonesname || !clonesname[0]) return 0;
204  type=TClass::GetClass(clonesname);
205  }
206  } else {
207  ::Warning("TVirtualBranchBrowsable::GetCollectionContainedType", "Neither branch nor parent given!");
208  return 0;
209  }
210 
211  if (!type) return 0;
212 
213  TBranch* branchNonCost=const_cast<TBranch*>(branch);
214  if (type->InheritsFrom(TClonesArray::Class())
215  && branch->IsA()==TBranchObject::Class()
216  && branchNonCost->GetListOfLeaves()
217  && branchNonCost->GetListOfLeaves()->GetEntriesFast()==1) {
218  // load first entry of the branch. Yes, this is bad, and might have
219  // unexpected side effects for the user, esp as already looking at
220  // (and not just drawing) a branch triggeres it.
221  // To prove just how ugly it is, we'll also have to const_cast the
222  // branch...
223  if (branch->GetReadEntry()==-1) branchNonCost->GetEntry(0);
224  // now get element
225  TLeafObject* lo = (TLeafObject*)branchNonCost->GetListOfLeaves()->First();
226  if (lo) {
227  TObject* objContainer = lo->GetObject();
228  if (objContainer && objContainer->IsA()==TClonesArray::Class()) {
229  contained = ((TClonesArray*)objContainer)->GetClass();
230  }
231  }
232  return type;
233  } else if (type->InheritsFrom(TClonesArray::Class())
234  && branch->IsA()==TBranchElement::Class()
235  && branchNonCost->GetListOfLeaves()
236  && branchNonCost->GetListOfLeaves()->GetEntriesFast()==1) {
237  // load first entry of the branch. Yes, this is bad, and might have
238  // unexpected side effects for the user, esp as already looking at
239  // (and not just drawing) a branch triggeres it.
240  // To prove just how ugly it is, we'll also have to const_cast the
241  // branch...
242 
243  //if (branch->GetReadEntry()==-1) branchNonCost->GetEntry(0);
244  // now get element
245  //TLeafObject* lo=(TLeafElement*)branchNonCost->GetListOfLeaves()->First();
246  //TObject* objContainer=(TObject*)((TBranchElement*)branch)->GetValuePointer();
247 
248  //if (objContainer && objContainer->IsA()==TClonesArray::Class())
249  // contained=((TClonesArray*)objContainer)->GetClass();
250 
251  // Currently we can peer into the nested TClonesArray, we need
252  // to update TBranchElement::GetValuePointer.
253  return type;
254  } else if (type->InheritsFrom(TCollection::Class())) {
255  // some other container, and we don't know what the contained type is
256  return type;
257  } else if (type->GetCollectionProxy()) {
258  contained=type->GetCollectionProxy()->GetValueClass();
259  return type;
260  } else if (type->InheritsFrom(TRef::Class()))
261  // we don't do TRefs, so return contained and container as 0
262  return 0;
263  else contained=type;
264  return 0;
265 }
266 
267 ////////////////////////////////////////////////////////////////////////////////
268 /// Return list of leaves. If not set up yet we'll create them.
269 
271 {
272  if (!fLeaves) {
273  TList* leaves=new TList();
274  leaves->SetOwner();
275  FillListOfBrowsables(*leaves, GetBranch(), this);
276  const_cast<TVirtualBranchBrowsable*>(this)->fLeaves=leaves;
277  }
278  return fLeaves;
279 }
280 
281 ////////////////////////////////////////////////////////////////////////////////
282 /// returns the list of registered generator methods
283 
284 std::list<TVirtualBranchBrowsable::MethodCreateListOfBrowsables_t>& TVirtualBranchBrowsable::GetRegisteredGenerators()
285 {
286  return fgGenerators;
287 }
288 
289 ////////////////////////////////////////////////////////////////////////////////
290 /// Returns the full name for TTree::Draw to draw *this.
291 /// Recursively appends, starting at the top TBranch,
292 /// all method / object names with proper reference operators (->, .)
293 /// depending on fTypeIsPointer.
294 
296 {
297  if (fParent)
298  fParent->GetScope(scope);
299  else {
300  scope=fBranch->GetName();
301  Ssiz_t pos = scope.First('[');
302  if (pos != kNPOS) {
303  scope.Remove(pos);
304  }
305  if (!scope.EndsWith(".")) scope+=".";
306  const TBranch* mother=fBranch;
307  while (mother != mother->GetMother() && (mother=mother->GetMother())) {
308  TString nameMother(mother->GetName());
309  if (!nameMother.EndsWith(".")) {
310  scope.Prepend(".");
311  scope.Prepend(nameMother);
312  } else {
313  if (mother != mother->GetMother()) {
314  // If the mother is the top level mother
315  // and its ends ends with a ., the name is already
316  // embedded!
317  scope.Prepend(nameMother);
318  }
319  }
320  }
321  }
322  if (GetName() && GetName()[0]=='.')
323  scope+=(GetName()+1);
324  else
325  scope+=GetName();
326  if (fClass && !scope.EndsWith(".")) { // otherwise we're a leaf, and no delimiter is appended
327  if (fTypeIsPointer)
328  scope+="->";
329  else scope+=".";
330  }
331 }
332 
333 ////////////////////////////////////////////////////////////////////////////////
334 /// Adds the default generators. The user can remove any of them as follows:
335 ///
336 /// TMethodBrowsable::Unregister();
337 ///
338 /// which will cause the browser not to show any methods.
339 
341 {
342  if (fgGeneratorsSet) return;
343  // can't call RegisterGenerator - would be recusive infite loop
348 }
349 
350 ////////////////////////////////////////////////////////////////////////////////
351 /// Adds a generator to be called when browsing branches.
352 /// Called by the Register method, which should be implemented
353 /// for all derived classes (see e.g. TMethodBrowsable::Register())
354 
356 {
358  // make sure we're not adding another copy
359  fgGenerators.remove(generator);
360  fgGenerators.push_back(generator);
361 }
362 
363 ////////////////////////////////////////////////////////////////////////////////
364 /// Removes a generator from the list of generators to be called when
365 /// browsing branches. The user can remove any of the generators as follows:
366 ///
367 /// TMethodBrowsable::Unregister();
368 ///
369 /// which will cause the browser not to show any methods.
370 
372 {
374  fgGenerators.remove(generator);
375 }
376 
378 
379 ////////////////////////////////////////////////////////////////////////////////
380 /// \class TMethodBrowsable
381 /// \ingroup tree
382 ///
383 /// This helper object allows the browsing of methods of objects stored in
384 /// branches. They will be depicted by a leaf (or a branch, in case the method
385 /// returns an object) with a red exclamation mark. Only a subset of all
386 /// methods will be shown in the browser (see IsMethodBrowsable for the
387 /// criteria a method has to satisfy).
388 ///
389 /// Obviously, methods are only available if the library is loaded which
390 /// contains the dictionary for the class to be browsed!
391 ///
392 /// If a branch contains a collection, TMethodBrowsable tries to find out
393 /// what the contained element is (it will only create methods for the
394 /// contained elements, but never for the collection). If it fails to extract
395 /// the type of the contained elements, or if there is no guarantee that the
396 /// type has any other common denominator than TObject (e.g. in the case of
397 /// a TObjArray, which can hold any object deriving from TObject) no methods
398 /// will be added.
399 
400 ////////////////////////////////////////////////////////////////////////////////
401 /// Constructor.
402 /// Links a TBranchElement to a TMethod, allowing the TBrowser to
403 /// browse simple methods.
404 ///
405 /// The c'tor sets the name for a method "Class::Method(params) const"
406 /// to "Method(params)", title to TMethod::GetPrototype
407 
409  const TVirtualBranchBrowsable* parent /* =0 */):
410  TVirtualBranchBrowsable(branch, 0, kFALSE, parent), fMethod(m)
411 {
412  TString name(m->GetName());
413  name+="()";
414  if (name.EndsWith(" const")) name.Remove(name.Length()-6);
415  SetName(name);
416 
417  name=m->GetPrototype();
418  if (m->GetCommentString() && strlen(m->GetCommentString()))
419  name.Append(" // ").Append(m->GetCommentString());
420  SetTitle(name);
421 
422  TString plainReturnType(m->GetReturnTypeName());
423  if (plainReturnType.EndsWith("*")) {
425  plainReturnType.Remove(plainReturnType.Length()-1);
426  plainReturnType.Strip();
427  if(plainReturnType.BeginsWith("const")) {
428  plainReturnType.Remove(0,5);
429  plainReturnType.Strip();
430  }
431  }
432  SetType(TClass::GetClass(plainReturnType));
433 }
434 
435 ////////////////////////////////////////////////////////////////////////////////
436 /// Given a class, this methods fills list with TMethodBrowsables
437 /// for the class and its base classes, and returns the number of
438 /// added elements. If called from a TBranch::Browse overload, "branch"
439 /// should be set to the calling TBranch, otherwise "parent" should
440 /// be set to the TVirtualBranchBrowsable being browsed, and branch
441 /// should be the branch of the parent.
442 
444 {
445  if (!cl) return;
446  TList allClasses;
447  allClasses.Add(cl);
448 
449  if (cl->IsLoaded()) {
450  for(TObjLink* lnk=allClasses.FirstLink();
451  lnk; lnk=lnk->Next()) {
452  cl=(TClass*)lnk->GetObject();
453  TList* bases=cl->GetListOfBases();
454  TBaseClass* base;
455  TIter iB(bases);
456  while ((base=(TBaseClass*)iB())) {
457  TClass* bc=base->GetClassPointer();
458  if (bc) allClasses.Add(bc);
459  }
460  }
461  } else {
463  for(int el = 0; el < info->GetElements()->GetEntries(); ++el) {
464  TStreamerElement *element = (TStreamerElement *)info->GetElements()->UncheckedAt(el);
465  if (element->IsBase()) {
466  TClass *bc = element->GetClassPointer();
467  if (bc) allClasses.Add(bc);
468  }
469  }
470  }
471 
472  TList allMethods;
473  TIter iC(&allClasses);
474  while ((cl=(TClass*)iC())) {
475  TList* methods=cl->GetListOfMethods();
476  if (!methods) continue;
477  TMethod* method=0;
478  TIter iM(methods);
479  while ((method=(TMethod*)iM()))
480  if (method && !allMethods.FindObject(method->GetName()))
481  allMethods.Add(method);
482  }
483 
484  TIter iM(&allMethods);
485  TMethod* m=0;
486  while ((m=(TMethod*)iM())) {
488  li.Add(m);
489  }
490  }
491 }
492 
493 ////////////////////////////////////////////////////////////////////////////////
494 /// This methods fills list with TMethodBrowsables
495 /// for the branch's or parent's class and its base classes, and returns
496 /// the number of added elements. If called from a TBranch::Browse
497 /// overload, "branch" should be set to the calling TBranch, otherwise
498 /// "parent" should be set to the TVirtualBranchBrowsable being browsed.
499 
501  const TVirtualBranchBrowsable* parent /*=0*/)
502 {
503  TClass* cl;
504  // we don't care about collections, so only use the TClass argument,
505  // and not the return value
506  GetCollectionContainedType(branch, parent, cl);
507  if (!cl) return 0;
508 
509  TList listMethods;
510  GetBrowsableMethodsForClass(cl, listMethods);
511  TMethod* method=0;
512  TIter iMethods(&listMethods);
513  while ((method=(TMethod*)iMethods())) {
514  li.Add(new TMethodBrowsable(branch, method, parent));
515  }
516  return listMethods.GetSize();
517 }
518 
519 ////////////////////////////////////////////////////////////////////////////////
520 /// A TMethod is browsable if it is const, public and not pure virtual,
521 /// if does not have any parameter without default value, and if it has
522 /// a (non-void) return value.
523 /// A method called *, Get*, or get* will not be browsable if there is a
524 /// persistent data member called f*, _*, or m*, as data member access is
525 /// faster than method access. Examples: if one of fX, _X, or mX is a
526 /// persistent data member, the methods GetX(), getX(), and X() will not
527 /// be browsable.
528 
530 {
531  long property = m->Property();
532  if (m->GetNargs() - m->GetNargsOpt() == 0
533  && (property & kIsConstant)
534  && !(property & (kIsPrivate | kIsProtected | kIsPureVirtual) )
535  && m->GetReturnTypeName()
536  && strcmp("void",m->GetReturnTypeName())
537  && !strstr(m->GetName(),"DeclFile")
538  && !strstr(m->GetName(),"ImplFile")
539  && strcmp(m->GetName(),"IsA")
540  && strcmp(m->GetName(),"Class")
541  && strcmp(m->GetName(),"CanBypassStreamer")
542  && strcmp(m->GetName(),"Class_Name")
543  && strcmp(m->GetName(),"ClassName")
544  && strcmp(m->GetName(),"Clone")
545  && strcmp(m->GetName(),"DrawClone")
546  && strcmp(m->GetName(),"GetName")
547  && strcmp(m->GetName(),"GetDrawOption")
548  && strcmp(m->GetName(),"GetIconName")
549  && strcmp(m->GetName(),"GetOption")
550  && strcmp(m->GetName(),"GetTitle")
551  && strcmp(m->GetName(),"GetUniqueID")
552  && strcmp(m->GetName(),"Hash")
553  && strcmp(m->GetName(),"IsFolder")
554  && strcmp(m->GetName(),"IsOnHeap")
555  && strcmp(m->GetName(),"IsSortable")
556  && strcmp(m->GetName(),"IsZombie")) {
557 
558  // look for matching data member
559  TClass* cl=m->GetClass();
560  if (!cl) return kTRUE;
561  TList* members=cl->GetListOfDataMembers();
562  if (!members) return kTRUE;
563  const char* baseName=m->GetName();
564  if (!strncmp(m->GetName(), "Get", 3) ||
565  !strncmp(m->GetName(), "get", 3))
566  baseName+=3;
567  if (!baseName[0]) return kTRUE;
568 
569  TObject* mem=0;
570  const char* arrMemberNames[3]={"f%s","_%s","m%s"};
571  for (Int_t i=0; !mem && i<3; i++)
572  mem=members->FindObject(TString::Format(arrMemberNames[i],baseName));
573  return (!mem ||! ((TDataMember*)mem)->IsPersistent());
574  };
575  return kFALSE;
576 }
577 
578 ////////////////////////////////////////////////////////////////////////////////
579 /// Wrapper for the registration method. Needed against MSVC, which
580 /// assigned different addr to the same method, depending on what
581 /// translation unit you're in...
582 
584 {
586 }
587 
588 ////////////////////////////////////////////////////////////////////////////////
589 /// Wrapper for the registration method. Needed against MSVC, which
590 /// assigned different addr to the same method, depending on what
591 /// translation unit you're in...
592 
594 {
596 }
597 
599 
600 ////////////////////////////////////////////////////////////////////////////////
601 /// \class TNonSplitBrowsable
602 /// \ingroup tree
603 ///
604 /// Allows a TBrowser to browse non-split branches as if they were split. The
605 /// generator extracts the necessary information from the streamer info in
606 /// memory (which does not have to be the same as the one on file, in case
607 /// a library was loaded containing the dictionary for this type), i.e. it
608 /// also works without loading the class's library.
609 ///
610 /// Just as with TMethodBrowsables, if the generator finds a collection it
611 /// only takes the contained objects into account, not the collections. If
612 /// it identifies a collection, but cannot extract the contained type, or the
613 /// contained type can be anything deriving from a TObject (like for TObjArray)
614 /// or is not limited at all, no browser helper objects are created.
615 
616 ////////////////////////////////////////////////////////////////////////////////
617 /// Constructor. Creates a TNonSplitBrowsable from a TStreamerElement, containing branch
618 /// and (if applicable) parent TVirtualBranchBrowsable.
619 
621  const TVirtualBranchBrowsable* parent /* =0 */):
622  TVirtualBranchBrowsable(branch, element->GetClassPointer(),
623  element->IsaPointer(), parent)
624 {
625  SetNameTitle(element->GetName(), element->GetTitle());
626 }
627 
628 ////////////////////////////////////////////////////////////////////////////////
629 /// Given either a branch "branch" or a "parent" TVirtualBranchBrowsable, we fill
630 /// "list" with objects of type TNonSplitBrowsable which represent the members
631 /// of class "cl" (and its base classes' members).
632 
634  const TVirtualBranchBrowsable* parent /* =0 */)
635 {
636  // branch has to be unsplit, i.e. without sub-branches
637  if (parent==0
638  && (branch==0 ||
639  (const_cast<TBranch*>(branch)->GetListOfBranches()
640  && const_cast<TBranch*>(branch)->GetListOfBranches()->GetEntries()!=0)
641  )
642  ) {
643  return 0;
644  }
645  // we only expand our own parents
646  //if (parent && parent->IsA()!=TNonSplitBrowsable::Class()) return 0;
647 
648  TClass* clContained=0;
649  GetCollectionContainedType(branch, parent, clContained);
650  TVirtualStreamerInfo* streamerInfo= clContained?clContained->GetStreamerInfo():0;
651  if (!streamerInfo
652  || !streamerInfo->GetElements()
653  || !streamerInfo->GetElements()->GetSize()) return 0;
654 
655  if (!branch && parent) branch=parent->GetBranch();
656 
657  // we simply add all of our and the bases' members into one big list
658  TList myStreamerElementsToCheck;
659  myStreamerElementsToCheck.AddAll(streamerInfo->GetElements());
660 
661  Int_t numAdded=0;
662  TStreamerElement* streamerElement=0;
663  for (TObjLink *link = myStreamerElementsToCheck.FirstLink();
664  link;
665  link = link->Next() ) {
666  streamerElement = (TStreamerElement*)link->GetObject();
667  if (streamerElement->IsBase()) {
668  // this is a base class place holder
669  // continue with the base class's streamer info
670  TClass* base=streamerElement->GetClassPointer();
671  if (!base || !base->GetStreamerInfo()) continue;
672 
673  // add all of the base class's streamer elements
674  // (which in turn can be a base, which will be
675  // unfolded in a later iteration) to the list
676  TObjArray* baseElements=base->GetStreamerInfo()->GetElements();
677  if (!baseElements) continue;
678  TIter iBaseSE(baseElements);
679  TStreamerElement* baseSE=0;
680  while ((baseSE=(TStreamerElement*)iBaseSE()))
681  // we should probably check whether we're replacing something here...
682  myStreamerElementsToCheck.Add(baseSE);
683  } else if (!strcmp(streamerElement->GetName(),"This")
684  && !strcmp(clContained->GetName(), streamerElement->GetTypeName())) {
685  // this is a collection of the real elements.
686  // So get the class ptr for these elements...
687  TClass* clElements=streamerElement->GetClassPointer();
688  TVirtualCollectionProxy* collProxy=clElements?clElements->GetCollectionProxy():0;
689  clElements=collProxy?collProxy->GetValueClass():0;
690  if (!clElements) continue;
691 
692  // now loop over the class's streamer elements
693  streamerInfo = clElements->GetStreamerInfo();
694  if (streamerInfo) {
695  TIter iElem(streamerInfo->GetElements());
696  TStreamerElement* elem=0;
697  while ((elem=(TStreamerElement*)iElem())) {
698  TNonSplitBrowsable* nsb=new TNonSplitBrowsable(elem, branch, parent);
699  li.Add(nsb);
700  numAdded++;
701  }
702  } else {
703  ::Error("TNonSplitBrowsable::GetBrowsables",
704  "Missing the StreamerInfo for the class \"%s\" for the branch \"%s\" in the TTree \"%s\".",
705  clElements->GetName(), branch->GetName(), branch->GetTree()->GetName());
706  }
707  } else {
708  // we have a basic streamer element
709  TNonSplitBrowsable* nsb=new TNonSplitBrowsable(streamerElement, branch, parent);
710  li.Add(nsb);
711  numAdded++;
712  }
713  }
714  return numAdded;
715 }
716 
717 ////////////////////////////////////////////////////////////////////////////////
718 /// Wrapper for the registration method. Needed against MSVC, which
719 /// assigned different addr to the same method, depending on what
720 /// translation unit you're in...
721 
723 {
725 }
726 
727 ////////////////////////////////////////////////////////////////////////////////
728 /// Wrapper for the registration method. Needed against MSVC, which
729 /// assigned different addr to the same method, depending on what
730 /// translation unit you're in...
731 
733 {
735 }
736 
738 
739 ////////////////////////////////////////////////////////////////////////////////
740 /// \class TCollectionPropertyBrowsable
741 /// \ingroup tree
742 ///
743 /// A tiny browser helper object (and its generator) for adding a virtual
744 /// (as in "not actually part of the class", not in C++ virtual) "@size()"
745 /// method to a collection. For all collections that derive from
746 /// TCollection, or have a TVirtualCollectionProxy associated with them,
747 /// a leaf is created that allows access to the number of elements in the
748 /// collection. For TClonesArrays and types with an associated
749 /// TVirtualCollectionProxy, this forwards to TTreeFormula's
750 /// "@branch.size()" functionality. For all other collections, a method call
751 /// to the appropriate collection's member function is executed when drawing.
752 ///
753 /// These objects are of course only created for elements containing a
754 /// collection; the generator has no effect on any other elements.
755 
756 ////////////////////////////////////////////////////////////////////////////////
757 /// Browses a TCollectionPropertyBrowsable. The only difference to
758 /// the generic TVirtualBranchBrowsable::Browse is our fDraw
759 
761 {
762  GetBranch()->GetTree()->Draw(GetDraw(), "", b ? b->GetDrawOption() : "");
763  if (gPad) gPad->Update();
764 }
765 
766 ////////////////////////////////////////////////////////////////////////////////
767 /// If the element to browse (given by either parent of branch) contains
768 /// a collection (TClonesArray or something for which a TVirtualCollectionProxy
769 /// exists), we will add some special objects to the browser. For now there is
770 /// just one object "@size", returning the size of the collection (as in
771 /// std::list::size(), or TClonesArray::GetEntries()).
772 /// The objects we create are simply used to forward strings (like "@size") to
773 /// TTreeFormula via our Browse method. These strings are stored in fName.
774 
776  const TVirtualBranchBrowsable* parent /* =0 */)
777 {
778  TClass* clContained=0;
779  TClass* clCollection=GetCollectionContainedType(branch, parent, clContained);
780  if (!clCollection || !clContained) return 0;
781 
782  // Build the fDraw string. Start with our scope.
783  TString scope;
784  if (parent) {
785  parent->GetScope(scope);
786  branch=parent->GetBranch();
787  } else if (branch){
788  scope=branch->GetName();
789  scope+=".";
790  const TBranch* mother=branch;
791  while (mother != mother->GetMother() && (mother=mother->GetMother())) {
792  TString nameMother(mother->GetName());
793  if (!nameMother.EndsWith(".")) {
794  scope.Prepend(".");
795  scope.Prepend(nameMother);
796  } else {
797  if (mother != mother->GetMother()) {
798  // If the mother is the top level mother
799  // and its ends ends with a ., the name is already
800  // embedded!
801  scope.Prepend(nameMother);
802  }
803  }
804  }
805  } else {
806  ::Warning("TCollectionPropertyBrowsable::GetBrowsables", "Neither branch nor parent is set!");
807  return 0;
808  }
809 
810  // remove trailing delimiters
811  if (scope.EndsWith(".")) scope.Remove(scope.Length()-1, 1);
812  else if (scope.EndsWith("->")) scope.Remove(scope.Length()-2, 2);
813 
814  // now prepend "@" to the last element of the scope,
815  // to access the collection and not its containees
816  Ssiz_t lastDot=scope.Last('.');
817  Ssiz_t lastArrow=scope.Last('>'); // assuming there's no branch name containing ">"...
818  Ssiz_t lastPart=lastDot;
819  if (lastPart==kNPOS || (lastArrow!=kNPOS && lastPart<lastArrow))
820  lastPart=lastArrow;
821  if (lastPart==kNPOS) lastPart=0;
822  else lastPart++;
823 
824  TString size_title("size of ");
825  size_title += clCollection->GetName();
826  if (clContained) {
827  size_title += " of ";
828  size_title += clContained->GetName();
829  }
830 
831  if (clCollection->GetCollectionProxy() || clCollection==TClonesArray::Class()) {
832  // the collection is one for which TTree::Draw supports @coll.size()
833 
835  if ( clCollection->GetCollectionProxy() &&
836  ( (clCollection->GetCollectionProxy()->GetValueClass()==0)
837  ||(clCollection->GetCollectionProxy()->GetValueClass()->GetCollectionProxy()!=0
838  && clCollection->GetCollectionProxy()->GetValueClass()->GetCollectionProxy()->GetValueClass()==0)
839  )) {
840  // If the contained type is not a class, we need an explitcit handle to get to the data.
841  cpb = new TCollectionPropertyBrowsable("values", "values in the container",
842  scope, branch, parent);
843  li.Add(cpb);
844  }
845  scope.Insert(lastPart, "@");
846  cpb = new TCollectionPropertyBrowsable("@size", size_title,
847  scope+".size()", branch, parent);
848  li.Add(cpb);
849  return 1;
850  } // if a collection proxy or TClonesArray
851  else if (clCollection->InheritsFrom(TCollection::Class())) {
852  // generic TCollection - we'll build size() ourselves, by mapping
853  // it to the proper member function of the collection
854  if (clCollection->InheritsFrom(TObjArray::Class()))
855  scope+="@.GetEntries()";
856  else scope+="@.GetSize()";
858  new TCollectionPropertyBrowsable("@size", size_title, scope, branch, parent);
859  li.Add(cpb);
860  return 1;
861  }
862  return 0;
863 }
864 
865 ////////////////////////////////////////////////////////////////////////////////
866 /// Wrapper for the registration method. Needed against MSVC, which
867 /// assigned different addr to the same method, depending on what
868 /// translation unit you're in...
869 
871 {
873 }
874 
875 ////////////////////////////////////////////////////////////////////////////////
876 /// Wrapper for the registration method. Needed against MSVC, which
877 /// assigned different addr to the same method, depending on what
878 /// translation unit you're in...
879 
881 {
883 }
884 
886 
887 ////////////////////////////////////////////////////////////////////////////////
888 /// \class TCollectionMethodBrowsable
889 /// \ingroup tree
890 ///
891 /// TCollectionMethodBrowsable extends TCollectionPropertyBrowsable by showing
892 /// all methods of the collection itself. If none are available - e.g. for STL
893 /// classes like std::list, a TVirtualBranchBrowsable object is reated instead.
894 /// The methods' names will have a "@" prepended, to distinguish them from the
895 /// contained elements' methods.
896 ///
897 /// This browser helper object is not part of the default list of registered
898 /// generators (see TVirtualBranchBrowsable::RegisterDefaultGenerators()).
899 /// If you want to use it, you should call
900 /// TCollectionMethodBrowsable::Register();
901 /// As it extends the functionality of TVirtualBranchBrowsable, one might want
902 /// to unregister the generator of the "@size()" method by calling
903 /// TCollectionPropertyBrowsable::Unregister();
904 
905 ////////////////////////////////////////////////////////////////////////////////
906 /// Contructor, see TMethodBrowsable's constructor.
907 /// Prepends "@" to the name to make this method work on the container.
908 
910  const TVirtualBranchBrowsable* parent /*=0*/):
911 TMethodBrowsable(branch, m, parent)
912 {
913  SetName(TString("@")+GetName());
914 }
915 
916 ////////////////////////////////////////////////////////////////////////////////
917 /// This methods fills list with TMethodBrowsables
918 /// for the branch's or parent's collection class and its base classes,
919 /// and returns the number of added elements. If called from a TBranch::Browse
920 /// overload, "branch" should be set to the calling TBranch, otherwise
921 /// "parent" should be set to the TVirtualBranchBrowsable being browsed.
922 
924  const TVirtualBranchBrowsable* parent /*=0*/)
925 {
926  TClass* clContained=0;
927  // we don't care about the contained class, but only about the collections,
928  TClass* clContainer=GetCollectionContainedType(branch, parent, clContained);
929  if (!clContainer || !clContained) return 0;
930 
931  TList listMethods;
932  GetBrowsableMethodsForClass(clContainer, listMethods);
933  TMethod* method=0;
934  TIter iMethods(&listMethods);
935  while ((method=(TMethod*)iMethods()))
936  li.Add(new TCollectionMethodBrowsable(branch, method, parent));
937 
938  // if we have no methods, and if the class has a collection proxy, just add
939  // the corresponding TCollectionPropertyBrowsable instead.
940  // But only do that if TCollectionPropertyBrowsable is not generatated anyway
941  // - we don't need two of them.
942  if (!listMethods.GetSize() && clContainer->GetCollectionProxy()) {
943  std::list<MethodCreateListOfBrowsables_t>& listGenerators=GetRegisteredGenerators();
944  std::list<MethodCreateListOfBrowsables_t>::iterator iIsRegistered
945  = std::find(listGenerators.begin(), listGenerators.end(), &TCollectionPropertyBrowsable::GetBrowsables);
946  if (iIsRegistered==listGenerators.end()) {
948  return 1;
949  }
950  }
951  return listMethods.GetSize();
952 }
953 
954 ////////////////////////////////////////////////////////////////////////////////
955 /// Wrapper for the registration method. Needed against MSVC, which
956 /// assigned different addr to the same method, depending on what
957 /// translation unit you're in...
958 
960 {
962 }
963 
964 ////////////////////////////////////////////////////////////////////////////////
965 /// Wrapper for the registration method. Needed against MSVC, which
966 /// assigned different addr to the same method, depending on what
967 /// translation unit you're in...
968 
970 {
972 }
static void Unregister()
Wrapper for the registration method.
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition: TClass.cxx:3452
An array of TObjects.
Definition: TObjArray.h:37
static void RegisterDefaultGenerators()
Adds the default generators.
A Branch for the case of an object.
Definition: TBranchObject.h:26
const char * GetReturnTypeName() const
Get full type description of function return type, e,g.: "class TDirectory*".
Definition: TFunction.cxx:140
TObject * GetObject() const
Definition: TLeafObject.h:48
virtual TClass * GetValueClass() const =0
TList * fLeaves
pointer to leaves
All ROOT classes may have RTTI (run time type identification) support added.
Definition: TDataMember.h:31
const Ssiz_t kNPOS
Definition: RtypesCore.h:115
A TLeaf for a general object derived from TObject.
Definition: TLeafObject.h:31
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:640
TStreamerElement * GetElement(Int_t id) const
TClass * GetClassPointer(Bool_t load=kTRUE)
Get pointer to the base class TClass.
Definition: TBaseClass.cxx:63
static Int_t GetBrowsables(TList &list, const TBranch *branch, const TVirtualBranchBrowsable *parent=0)
This methods fills list with TMethodBrowsables for the branch&#39;s or parent&#39;s collection class and its ...
virtual TClass * GetClassPointer() const
Returns a pointer to the TClass of this element.
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition: TNamed.cxx:131
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
TVirtualStreamerInfo * GetStreamerInfo(Int_t version=0) const
returns a pointer to the TVirtualStreamerInfo object for version If the object does not exist...
Definition: TClass.cxx:4360
virtual void AddAll(const TCollection *col)
Add all objects from collection col to this collection.
Definition: TCollection.cxx:58
static Int_t GetBrowsables(TList &list, const TBranch *branch, const TVirtualBranchBrowsable *parent=0)
This methods fills list with TMethodBrowsables for the branch&#39;s or parent&#39;s class and its base classe...
TStreamerInfo * GetInfo() const
Get streamer info for the branch class.
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition: TClass.cxx:3562
Basic string class.
Definition: TString.h:129
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition: TClass.cxx:2814
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
static void Register()
Wrapper for the registration method.
Int_t GetNelement() const
TString & Prepend(const char *cs)
Definition: TString.h:609
virtual EDataType GetType() const =0
virtual void SetNameTitle(const char *name, const char *title)
Set all the TNamed parameters (name and title).
Definition: TNamed.cxx:145
TString & Insert(Ssiz_t pos, const char *s)
Definition: TString.h:597
const TVirtualBranchBrowsable * fParent
parent method if this method is member of a returned class
A tiny browser helper object (and its generator) for adding a virtual (as in "not actually part of th...
virtual TObject * FindObject(const char *name) const
Find an object in this list using its name.
Definition: TList.cxx:501
void SetType(TClass *type)
static Int_t FillListOfBrowsables(TList &list, const TBranch *branch, const TVirtualBranchBrowsable *parent=0)
Askes all registered generators to fill their browsables into the list.
void GetScope(TString &scope) const
Returns the full name for TTree::Draw to draw *this.
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString...
Definition: TString.cxx:2345
static void GetBrowsableMethodsForClass(TClass *cl, TList &list)
Given a class, this methods fills list with TMethodBrowsables for the class and its base classes...
void Class()
Definition: Class.C:29
Int_t(* MethodCreateListOfBrowsables_t)(TList &, const TBranch *branch, const TVirtualBranchBrowsable *parent)
static void Unregister()
Wrapper for the registration method.
TNonSplitBrowsable(const TStreamerElement *element, const TBranch *branch, const TVirtualBranchBrowsable *parent=0)
Constructor.
TList * GetLeaves() const
Return list of leaves. If not set up yet we&#39;ll create them.
Bool_t IsLoaded() const
Return true if the shared library of this class is currently in the a process&#39;s memory.
Definition: TClass.cxx:5549
TCollectionMethodBrowsable(const TBranch *branch, TMethod *m, const TVirtualBranchBrowsable *parent=0)
Contructor, see TMethodBrowsable&#39;s constructor.
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition: TString.cxx:2231
Long_t Property() const
Get property description word. For meaning of bits see EProperty.
Definition: TFunction.cxx:183
Int_t GetID() const
Ssiz_t First(char c) const
Find first occurrence of a character c.
Definition: TString.cxx:477
virtual const char * GetCommentString()
Returns a comment string from the class declaration.
Definition: TMethod.cxx:105
virtual const char * GetClonesName() const
A doubly linked list.
Definition: TList.h:43
static TClass * GetCollectionContainedType(const TBranch *b, const TVirtualBranchBrowsable *parent, TClass *&contained)
Check whether the branch (or the parent) contains a collection.
static void Register()
Wrapper for the registration method.
TObject * First() const
Return the object in the first slot.
Definition: TObjArray.cxx:471
Using a TBrowser one can browse all ROOT objects.
Definition: TBrowser.h:37
Int_t GetNargs() const
Number of function arguments.
Definition: TFunction.cxx:164
TClass * fClass
pointer to TClass representing our type (i.e. return type for methods), 0 if basic type ...
static Bool_t IsMethodBrowsable(const TMethod *m)
A TMethod is browsable if it is const, public and not pure virtual, if does not have any parameter wi...
static void RegisterGenerator(MethodCreateListOfBrowsables_t generator)
Adds a generator to be called when browsing branches.
TMarker * m
Definition: textangle.C:8
Int_t GetEntriesFast() const
Definition: TObjArray.h:64
TCollectionMethodBrowsable extends TCollectionPropertyBrowsable by showing all methods of the collect...
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:873
static Int_t GetBrowsables(TList &list, const TBranch *branch, const TVirtualBranchBrowsable *parent=0)
Given either a branch "branch" or a "parent" TVirtualBranchBrowsable, we fill "list" with objects of ...
Ssiz_t Length() const
Definition: TString.h:388
virtual Int_t GetEntry(Long64_t entry=0, Int_t getall=0)
Read all leaves of entry and return total number of bytes read.
Definition: TBranch.cxx:1291
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:71
static void Unregister()
Wrapper for the registration method.
Bool_t InheritsFrom(const char *cl) const
Return kTRUE if this class inherits from a class with name "classname".
Definition: TClass.cxx:4602
static Bool_t fgGeneratorsSet
have we set the generators yet? empty is not good enough - user might have removed them ...
TMethodBrowsable(const TBranch *branch, TMethod *m, const TVirtualBranchBrowsable *parent=0)
Constructor.
TVirtualBranchBrowsable is a base class (not really abstract, but useless by itself) for helper objec...
Each class (see TClass) has a linked list of its base class(es).
Definition: TBaseClass.h:31
virtual TObjLink * FirstLink() const
Definition: TList.h:97
A Branch for the case of an object.
const Bool_t kFALSE
Definition: RtypesCore.h:92
void SetTypeIsPointer(Bool_t set=kTRUE)
TString & Remove(Ssiz_t pos)
Definition: TString.h:621
int Ssiz_t
Definition: RtypesCore.h:63
TObject * UncheckedAt(Int_t i) const
Definition: TObjArray.h:89
#define ClassImp(name)
Definition: Rtypes.h:336
const TBranch * GetBranch() const
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition: TString.cxx:875
virtual TObjArray * GetElements() const =0
TClass * GetClassType() const
virtual const char * GetClassName() const
Return the name of the user class whose content is stored in this branch, if any. ...
int type
Definition: TGX11.cxx:120
void Browse(TBrowser *b)
Calls TTree::Draw on the method if return type is not a class; otherwise expands returned object&#39;s "f...
virtual void Draw(Option_t *opt)
Default Draw method for all objects.
Definition: TTree.h:355
virtual Bool_t IsBase() const
Return kTRUE if the element represent a base class.
virtual const char * GetPrototype() const
Returns the prototype of a function as defined by CINT, or 0 in case of error.
Definition: TFunction.cxx:245
void Browse(TBrowser *b)
Browse this collection (called by TBrowser).
TObjArray * GetListOfLeaves()
Definition: TBranch.h:183
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2885
virtual const char * GetClassName() const
Return the name of the user class whose content is stored in this branch, if any. ...
Definition: TBranchObject.h:43
Mother of all ROOT objects.
Definition: TObject.h:37
static void Register()
Wrapper for the registration method.
An array of clone (identical) objects.
Definition: TClonesArray.h:32
const char * GetTypeName() const
static Int_t GetBrowsables(TList &list, const TBranch *branch, const TVirtualBranchBrowsable *parent=0)
If the element to browse (given by either parent of branch) contains a collection (TClonesArray or so...
virtual void Add(TObject *obj)
Definition: TList.h:77
Each ROOT class (see TClass) has a linked list of methods.
Definition: TMethod.h:38
Long64_t GetReadEntry() const
Definition: TBranch.h:174
you should not use this method at all Int_t Int_t Double_t Double_t Double_t Int_t Double_t Double_t Double_t Double_t b
Definition: TRolke.cxx:630
TClass * GetClass() const
Definition: TMethod.h:55
TTree * GetTree() const
Definition: TBranch.h:188
Int_t GetEntries() const
Return the number of objects in array (i.e.
Definition: TObjArray.cxx:494
#define gPad
Definition: TVirtualPad.h:284
Definition: tree.py:1
static void Unregister()
Wrapper for the registration method.
const TBranch * fBranch
pointer to the branch element representing the top object
A TTree object has a header with a name and a title.
Definition: TTree.h:78
static std::list< MethodCreateListOfBrowsables_t > fgGenerators
list of MethodCreateListOfBrowsables_t called by CreateListOfBrowsables
Option_t * GetDrawOption() const
Get option used by the graphics system to draw this object.
Definition: TBrowser.h:104
Bool_t fTypeIsPointer
return type is pointer to class
static void Register()
Wrapper for the registration method.
void Browse(TBrowser *b)
Browses a TCollectionPropertyBrowsable.
virtual Int_t GetSize() const
Definition: TCollection.h:89
A TTree is a list of TBranches.
Definition: TBranch.h:57
Abstract Interface class describing Streamer information for one class.
static std::list< MethodCreateListOfBrowsables_t > & GetRegisteredGenerators()
returns the list of registered generator methods
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition: TNamed.cxx:155
TList * GetListOfMethods(Bool_t load=kTRUE)
Return list containing the TMethods of a class.
Definition: TClass.cxx:3611
const Bool_t kTRUE
Definition: RtypesCore.h:91
~TVirtualBranchBrowsable()
Destructor. Delete our leaves.
Int_t GetNargsOpt() const
Number of function optional (default) arguments.
Definition: TFunction.cxx:174
TBranch * GetMother() const
Get our top-level parent branch in the tree.
Definition: TBranch.cxx:1625
TVirtualBranchBrowsable(const TBranch *b, TClass *type, Bool_t typeIsPointer, const TVirtualBranchBrowsable *parent=0)
Constructor setting all members according to parameters.
Allows a TBrowser to browse non-split branches as if they were split.
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:859
virtual const char * GetTitle() const
Returns title of object.
Definition: TNamed.h:48
static void UnregisterGenerator(MethodCreateListOfBrowsables_t generator)
Removes a generator from the list of generators to be called when browsing branches.
This helper object allows the browsing of methods of objects stored in branches.