1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 
6 /*!
7  ******************************************************************************
8  *
9  * \file Group.hpp
10  *
11  * \brief   Header file containing definition of Group class.
12  *
13  ******************************************************************************
14  */
15 
16 #ifndef SIDRE_GROUP_HPP_
17 #define SIDRE_GROUP_HPP_
18 
19 // axom headers
20 #include "axom/config.hpp"
21 #include "axom/core/Macros.hpp"
22 #include "axom/core/Types.hpp"
23 #include "axom/slic.hpp"
24 
25 // Standard C++ headers
26 #include <memory>
27 #include <map>
28 #include <string>
29 #include <vector>
30 #include <set>
31 #include <cstring>
32 
33 // third party lib headers
34 #ifdef AXOM_USE_HDF5
35   #include "hdf5.h"
36 #endif
37 
38 // Sidre headers
39 #include "SidreTypes.hpp"
40 #include "View.hpp"
41 
42 // Define the default protocol for sidre I/O
43 #ifdef AXOM_USE_HDF5
44   #define SIDRE_DEFAULT_PROTOCOL "sidre_hdf5"
45 #else
46   #define SIDRE_DEFAULT_PROTOCOL "sidre_conduit_json"
47 #endif
48 
49 namespace axom
50 {
51 namespace sidre
52 {
53 class Buffer;
54 class Group;
55 class DataStore;
56 template <typename TYPE>
57 class ItemCollection;
58 
59 /*!
60  * \class Group
61  *
62  * \brief Group holds a collection of Views and (child) Groups.
63  *
64  * The Group class has the following properties:
65  *
66  *    - Groups can be organized into a (tree) hierarchy by creating
67  *      child Groups from the root Group owned by a DataStore object.
68  *    - A Group object can only be created by another Group; the
69  *      Group ctor is not visible externally. A Group is owned
70  *      by the Group that creates it (its parent) and becomes a
71  *      (direct) child Group of the parent. Groups in the subtree
72  *      rooted at an ancestor Group are that Group's descendants.
73  *    - A Group object has a unique name (string) within its parent
74  *      Group.
75  *    - A Group object maintains a pointer to its parent Group.
76  *    - A Group object can be moved or copied to another Group.
77  *    - Group objects can create View objects within them. The
78  *      Group that creates a View owns it.
79  *    - A View object has a unique name (string) within the Group
80  *      that owns it.
81  *    - A View object can be moved or copied to another Group.
82  *
83  * Note that Views and child Groups within a Group can be accessed
84  * by name or index.
85  *
86  * Note that certain methods for querying, creating, retrieving, and
87  * deleting Groups and Views take a string with path syntax,
88  * while others take the name of a direct child of the current Group.
89  * Methods that require the name of a direct child are marked with
90  * "Child", for example hasChildView() and hasChildGroup().  When a path
91  * string is passed to a method that accepts path syntax, the last item in
92  * the path indicates the item to be created, accessed, etc.  For example,
93  *
94  *      View* view = group->createView("foo/bar/baz");
95  *
96  * is equivalent to
97  *
98  *      View* view =
99  *        group->createGroup("foo")->createGroup("bar")->createView("baz");
100  *
101  * In particular, intermediate Groups "foo" and "bar" will be created in
102  * this case if they don't already exist.
103  *
104  * Methods that access Views or Groups by index work with the direct
105  * children of the current Group because an index has no meaning outside
106  * of the indexed group.  None of these methods is marked with "Child".
107  *
108  * A Group can optionally be created to hold items in a "list format". In
109  * this format, any number of child Group or View items can be created with
110  * empty strings for their names, and none of the methods that access
111  * child items by name or path will return a valid pointer.
112  *
113  * \attention when Views or Groups are created, destroyed, copied, or moved,
114  * indices of other Views and Groups in associated Group objects may
115  * become invalid. This is analogous to iterator invalidation for STL
116  * containers when the container contents change.
117  *
118  */
119 class Group
120 {
121 public:
122   //
123   // Friend declarations to constrain usage via controlled access to
124   // private members.
125   //
126   friend class DataStore;
127   friend class View;
128 
129   //@{
130   //!  @name Basic query and accessor methods.
131 
132   /*!
133    * \brief Return the path delimiter
134    */
getPathDelimiter() const135   char getPathDelimiter() const { return s_path_delimiter; }
136 
137   /*!
138    * \brief Return index of Group object within parent Group.
139    */
getIndex() const140   IndexType getIndex() const { return m_index; }
141 
142   /*!
143    * \brief Return const reference to name of Group object.
144    *
145    * \sa getPath(), getPathName()
146    */
getName() const147   const std::string& getName() const { return m_name; }
148 
149   /*!
150    * \brief Return path of Group object, not including its name.
151    *
152    * \sa getName(), getPathName()
153    */
154   std::string getPath() const;
155 
156   /*!
157    * \brief Return full path of Group object, including its name.
158    *
159    * If a DataStore contains a Group tree structure a/b/c/d/e, the
160    * following results are expected:
161    *
162    * Method Call      | Result
163    * -----------------|----------
164    * e->getName()     | e
165    * e->getPath()     | a/b/c/d
166    * e->getPathName() | a/b/c/d/e
167    *
168    * \sa getName(), getPath(), View::getPathName()
169    */
getPathName() const170   std::string getPathName() const
171   {
172     const auto path = getPath();
173 
174     if(path.length() < 1)
175     {
176       return getName();
177     }
178 
179     return path + getPathDelimiter() + getName();
180   }
181 
182   /*!
183    * \brief Return pointer to non-const parent Group of a Group.
184    *
185    * Note that if this method is called on the root Group in a
186    * DataStore, a pointer to itself is returned.
187    * This allows root->getParent()->getParent() to always work similar
188    * to how the filesystem's `cd /; cd ../..` works.
189    */
getParent()190   Group* getParent() { return m_parent; }
191 
192   /*!
193    * \brief Return pointer to const parent Group of a Group.
194    *
195    * Note that if this method is called on the root Group in a
196    * DataStore, a pointer to itself is returned.
197    * This allows root->getParent()->getParent() to always work similar
198    * to how the filesystem's `cd /; cd ../..` works.
199    */
getParent() const200   const Group* getParent() const { return m_parent; }
201 
202   /*!
203    * \brief Return number of child Groups in a Group object.
204    */
205   IndexType getNumGroups() const;
206 
207   /*!
208    * \brief Return number of Views owned by a Group object.
209    */
210   IndexType getNumViews() const;
211 
212   /*!
213    * \brief Return pointer to non-const DataStore object that owns this
214    * object.
215    */
getDataStore()216   DataStore* getDataStore() { return m_datastore; }
217 
218   /*!
219    * \brief Return pointer to const DataStore object that owns this
220    * object.
221    */
getDataStore() const222   const DataStore* getDataStore() const { return m_datastore; }
223 
224   /*!
225    * \brief Return true if this Group is the DataStore's root Group.
226    */
isRoot() const227   bool isRoot() const { return m_parent == this; }
228 
229 #ifdef AXOM_USE_UMPIRE
230 
231   /*!
232    * \brief Return the ID of the default umpire::Allocator associated with this
233    * Group.
234    */
getDefaultAllocatorID() const235   int getDefaultAllocatorID() const { return m_default_allocator_id; }
236 
237   /*!
238    * \brief Return the default umpire::Allocator associated with this Group.
239    */
getDefaultAllocator() const240   umpire::Allocator getDefaultAllocator() const
241   {
242     umpire::ResourceManager& rm = umpire::ResourceManager::getInstance();
243     return rm.getAllocator(m_default_allocator_id);
244   }
245 
246   /*!
247    * \brief Set the default umpire::Allocator associated with this Group.
248    */
setDefaultAllocator(umpire::Allocator alloc)249   Group* setDefaultAllocator(umpire::Allocator alloc)
250   {
251     m_default_allocator_id = alloc.getId();
252     return this;
253   }
254 
255   /*!
256    * \brief Set the default umpire::Allocator associated with this Group.
257    */
setDefaultAllocator(int allocId)258   Group* setDefaultAllocator(int allocId)
259   {
260     m_default_allocator_id = allocId;
261     return this;
262   }
263 #endif
264 
265   //@}
266 
267   //@{
268   //!  @name View query methods.
269 
270   /*!
271    * \brief Return true if Group includes a descendant View with
272    * given name or path; else false.
273    */
274   bool hasView(const std::string& path) const;
275 
276   /*!
277    * \brief Return true if this Group owns a View with given name (not path);
278    * else false.
279    */
280   bool hasChildView(const std::string& name) const;
281 
282   /*!
283    * \brief Return true if this Group owns a View with given index; else false.
284    */
285   bool hasView(IndexType idx) const;
286 
287   /*!
288    * \brief Return index of View with given name owned by this Group object.
289    *
290    *        If no such View exists, return sidre::InvalidIndex;
291    */
292   IndexType getViewIndex(const std::string& name) const;
293 
294   /*!
295    * \brief Return name of View with given index owned by Group object.
296    *
297    *        If no such View exists, return sidre::InvalidName.
298    */
299   const std::string& getViewName(IndexType idx) const;
300 
301   //@}
302 
303   //@{
304   //!  @name View access methods.
305 
306   /*!
307 
308    * \brief Return pointer to non-const View with given name or path.
309    *
310    * This method requires that all groups in the path exist if a path is given.
311    *
312    * If no such View exists, nullptr is returned.
313    */
314   View* getView(const std::string& path);
315 
316   /*!
317    * \brief Return pointer to const View with given name or path.
318    *
319    * This method requires that all Groups in the path exist if a path is given.
320    *
321    * If no such View exists, nullptr is returned.
322    */
323   const View* getView(const std::string& path) const;
324 
325   /*!
326    * \brief Return pointer to non-const View with given index.
327    *
328    * If no such View exists, nullptr is returned.
329    */
330   View* getView(IndexType idx);
331 
332   /*!
333    * \brief Return pointer to const View with given index.
334    *
335    * If no such View exists, nullptr is returned.
336    */
337   const View* getView(IndexType idx) const;
338 
339   //@}
340 
341   //@{
342   //!  @name View iteration methods.
343   //!
344   //! Using these methods, a code can get the first View index and each
345   //! succeeding index.  This allows View iteration using the same
346   //! constructs in C++, C, and Fortran.  Example:
347   //!
348   //!      sidre::IndexType idx = grp->getFirstValidViewIndex();
349   //!      while( sidre::indexIsValid(idx) )
350   //!      {
351   //!          View* view = grp->getView(idx);
352   //!
353   //!          /// code here using view
354   //!
355   //!          idx = grp -> getNextValidViewIndex(idx);
356   //!      }
357 
358   /*!
359    * \brief Return first valid View index in Group object
360    *        (i.e., smallest index over all Views).
361    *
362    * sidre::InvalidIndex is returned if Group has no Views.
363    *
364    * \sa sidre::IndexType
365    * \sa sidre::indexIsValid()
366    */
367   IndexType getFirstValidViewIndex() const;
368 
369   /*!
370    * \brief Return next valid View index in Group object after given index
371    *        (i.e., smallest index over all View indices larger than given one).
372    *
373    * sidre::InvalidIndex is returned if there is no valid index greater
374    * than given one.
375    *
376    * \sa sidre::IndexType
377    * \sa sidre::indexIsValid()
378    */
379   IndexType getNextValidViewIndex(IndexType idx) const;
380 
381   //@}
382 
383   //@{
384   //!  @name Methods to create a View that has no associated data.
385   //!
386   //! \attention These methods do not allocate data or associate a View
387   //! with data. Thus, to do anything useful with a View created by one
388   //! of these methods, the View should be allocated, attached to a Buffer
389   //! or attached to externally-owned data.
390   //!
391   //! Each of these methods is a no-op if the given View name is an
392   //! empty string or the Group already has a View with given name or path.
393   //!
394   //! Additional conditions under which a method can be a no-op are described
395   //! for each method.
396 
397   /*!
398    * \brief Create an undescribed (i.e., empty) View object with given name
399    * or path in this Group.
400    *
401    * If path is an empty string, an unnamed view can be created only if
402    * this Group was created to hold items in a list format.  Otherwise
403    * an empty string will result in a nullptr being returned.
404    *
405    * \return pointer to new View object or nullptr if one is not created.
406    */
407   View* createView(const std::string& path);
408 
409   /*!
410    * \brief Create View object with given name or path in this Group that
411    *  has a data description with data type and number of elements.
412    *
413    * If given data type is undefined, or given number of elements is < 0,
414    * method is a no-op.
415    *
416    * \return pointer to new View object or nullptr if one is not created.
417    */
418   View* createView(const std::string& path, TypeID type, IndexType num_elems);
419 
420   /*!
421    * \brief Create View object with given name or path in this Group that
422    *  has a data description with data type and shape.
423    *
424    * If given data type is undefined, or given number of dimensions is < 0,
425    * or given shape ptr is null, method is a no-op.
426    *
427    * \return pointer to new View object or nullptr if one is not created.
428    */
429   View* createView(const std::string& path,
430                    TypeID type,
431                    int ndims,
432                    const IndexType* shape);
433 
434   /*!
435    * \brief Create View object with given name or path in this Group that
436    *  is described by a Conduit DataType object.
437    *
438    * \return pointer to new View object or nullptr if one is not created.
439    */
440   View* createView(const std::string& path, const DataType& dtype);
441 
442   //@}
443 
444   //@{
445   //!  @name Methods to create a View with a Buffer attached.
446   //!
447   //! \attention The Buffer passed to each of these methods may or may not
448   //! be allocated. Thus, to do anything useful with a View created by one
449   //! of these methods, the Buffer must be allocated and it must be compatible
450   //! with the View data description.
451   //!
452   //! Each of these methods is a no-op if Group already has a View or child
453   //! Group with the given name or path.
454   //!
455   //! If this Group was created to hold items in list format, the path can
456   //! be an empty string. Otherwise an empty string for the path will result
457   //! in a no-op.
458   //!
459   //! Also, calling one of these methods with a null Buffer pointer is
460   //! similar to creating a View with no data association.
461   //!
462   //! Additional conditions under which a method can be a no-op are described
463   //! for each method.
464 
465   /*!
466    * \brief Create an undescribed View object with given name or path in
467    * this Group and attach given Buffer to it.
468    *
469    * \attention The View cannot be used to access data in Buffer until it
470    * is described by calling a View::apply() method.
471    *
472    * This method is equivalent to:
473    * group->createView(name)->attachBuffer(buff).
474    *
475    * \return pointer to new View object or nullptr if one is not created.
476    *
477    * \sa View::attachBuffer()
478    */
479   View* createView(const std::string& path, Buffer* buff);
480 
481   /*!
482    * \brief Create View object with given name or path in this Group that
483    * has a data description with data type and number of elements and
484    * attach given Buffer to it.
485    *
486    * If given data type is undefined, or given number of elements is < 0,
487    * method is a no-op.
488    *
489    * This method is equivalent to:
490    * group->createView(name, type, num_elems)->attachBuffer(buff), or
491    * group->createView(name)->attachBuffer(buff)->apply(type, num_elems).
492    *
493    * \return pointer to new View object or nullptr if one is not created.
494    *
495    * \sa View::attachBuffer()
496    */
497   View* createView(const std::string& path,
498                    TypeID type,
499                    IndexType num_elems,
500                    Buffer* buff);
501 
502   /*!
503    * \brief Create View object with given name or path in this Group that
504    * has a data description with data type and shape and attach given
505    * Buffer to it.
506    *
507    * If given data type is undefined, or given number of dimensions is < 0,
508    * or given shape ptr is null, method is a no-op.
509    *
510    * This method is equivalent to:
511    * group->createView(name, type, ndims, shape)->attachBuffer(buff), or
512    * group->createView(name)->attachBuffer(buff)->apply(type, ndims, shape).
513    *
514    * \return pointer to new View object or nullptr if one is not created.
515    *
516    * \sa View::attachBuffer()
517    */
518   View* createView(const std::string& path,
519                    TypeID type,
520                    int ndims,
521                    const IndexType* shape,
522                    Buffer* buff);
523 
524   /*!
525    * \brief Create View object with given name or path in this Group that
526    *  is described by a Conduit DataType object and attach given Buffer to it.
527    *
528    * This method is equivalent to:
529    * group->createView(name, dtype)->attachBuffer(buff), or
530    * group->createView(name)->attachBuffer(buff)->apply(dtype).
531    *
532    * \return pointer to new View object or nullptr if one is not created.
533    *
534    * \sa View::attachBuffer()
535    */
536   View* createView(const std::string& path, const DataType& dtype, Buffer* buff);
537 
538   //@}
539 
540   //@{
541   //!  @name Methods to create a View with externally-owned data attached.
542   //!
543   //! \attention To do anything useful with a View created by one of these
544   //! methods, the external data must be allocated and compatible with the
545   //! View description.
546   //!
547   //! Each of these methods is a no-op if the given View name is an
548   //! empty string or the Group already has a View with given name or path.
549   //!
550   //! Additional conditions under which a method can be a no-op are described
551   //! for each method.
552 
553   /*!
554    * \brief Create View object with given name with given name or path in
555    * this Group and attach external data ptr to it.
556    *
557    * \attention Note that the View is "opaque" (it has no knowledge of
558    * the type or structure of the data) until a View::apply() method
559    * is called.
560    *
561    * This method is equivalent to:
562    * group->createView(name)->setExternalDataPtr(external_ptr).
563    *
564    * \return pointer to new View object or nullptr if one is not created.
565    *
566    * \sa View::setExternalDataPtr()
567    */
568   View* createView(const std::string& path, void* external_ptr);
569 
570   /*!
571    * \brief Create View object with given name or path in this Group that
572    * has a data description with data type and number of elements and
573    * attach externally-owned data to it.
574    *
575    * If given data type is undefined, or given number of elements is < 0,
576    * method is a no-op.
577    *
578    * This method is equivalent to:
579    * group->createView(name, type, num_elems)->setExternalDataPtr(external_ptr),
580    * or group->createView(name)->setExternalDataPtr(external_ptr)->
581    *           apply(type, num_elems).
582    *
583    * \return pointer to new View object or nullptr if one is not created.
584    *
585    * \sa View::setExternalDataPtr()
586    */
587   View* createView(const std::string& path,
588                    TypeID type,
589                    IndexType num_elems,
590                    void* external_ptr);
591 
592   /*!
593    * \brief Create View object with given name or path in this Group that
594    * has a data description with data type and shape and attach
595    * externally-owned data to it.
596    *
597    * If given data type is undefined, or given number of dimensions is < 0,
598    * or given shape ptr is null, method is a no-op.
599    *
600    * This method is equivalent to:
601    * group->createView(name, type, ndims, shape)->
602    *        setExternalDataPtr(external_ptr), or
603    * group->createView(name)->setExternalDataPtr(external_ptr)->
604    *        apply(type, ndims, shape).
605    *
606    * \return pointer to new View object or nullptr if one is not created.
607    *
608    * \sa View::setExternalDataPtr()
609    */
610   View* createView(const std::string& path,
611                    TypeID type,
612                    int ndims,
613                    const IndexType* shape,
614                    void* external_ptr);
615   /*!
616    * \brief Create View object with given name or path in this Group that
617    * is described by a Conduit DataType object and attach externally-owned
618    * data to it.
619    *
620    * This method is equivalent to:
621    * group->createView(name, dtype)->setExternalDataPtr(external_ptr), or
622    * group->createView(name)->setExternalDataPtr(external_ptr)->apply(dtype).
623    *
624    * \return pointer to new View object or nullptr if one is not created.
625    *
626    * \sa View::attachBuffer()
627    */
628   View* createView(const std::string& path,
629                    const DataType& dtype,
630                    void* external_ptr);
631 
632   //@}
633 
634   //@{
635   //!  @name Methods to create a View and allocate data for it.
636   //!
637   //! Each of these methods is a no-op if the given View name is an
638   //! empty string or the Group already has a View with given name or path.
639   //!
640   //! Additional conditions under which a method can be a no-op are described
641   //! for each method.
642 
643   /*!
644    * \brief Create View object with given name or path in this Group that
645    * has a data description with data type and number of elements and
646    * allocate data for it.
647    *
648    * If given data type is undefined, or given number of elements is < 0,
649    * method is a no-op.
650    *
651    * This is equivalent to: createView(name)->allocate(type, num_elems), or
652    * createView(name, type, num_elems)->allocate()
653    *
654    * \return pointer to new View object or nullptr if one is not created.
655    *
656    * \sa View::allocate()
657    */
658   View* createViewAndAllocate(const std::string& path,
659                               TypeID type,
660                               IndexType num_elems,
661                               int allocID = INVALID_ALLOCATOR_ID);
662 
663   /*!
664    * \brief Create View object with given name or path in this Group that
665    * has a data description with data type and shape and allocate data for it.
666    *
667    * If given data type is undefined, or given number of dimensions is < 0,
668    * or given shape ptr is null, method is a no-op.
669    *
670    * This method is equivalent to:
671    * group->createView(name)->allocate(type, ndims, shape), or
672    * createView(name, type, ndims, shape)->allocate().
673    *
674    * \return pointer to new View object or nullptr if one is not created.
675    *
676    * \sa View::allocate()
677    */
678   View* createViewAndAllocate(const std::string& path,
679                               TypeID type,
680                               int ndims,
681                               const IndexType* shape,
682                               int allocID = INVALID_ALLOCATOR_ID);
683 
684   /*!
685    * \brief Create View object with given name or path in this Group that
686    * is described by a Conduit DataType object and allocate data for it.
687    *
688    * This method is equivalent to:
689    * group->createView(name)->allocate(dtype), or
690    * group->createView(name, dtype)->allocate().
691    *
692    * If given data type object is empty, data will not be allocated.
693    *
694    * \return pointer to new View object or nullptr if one is not created.
695    *
696    * \sa View::allocate()
697    */
698   View* createViewAndAllocate(const std::string& path,
699                               const DataType& dtype,
700                               int allocID = INVALID_ALLOCATOR_ID);
701 
702   /*!
703    * \brief Create View object with given name or path in this Group
704    * set its data to given scalar value.
705    *
706    * This is equivalent to: createView(name)->setScalar(value);
707    *
708    * If given data type object is empty, data will not be allocated.
709    *
710    * \return pointer to new View object or nullptr if one is not created.
711    *
712    * \sa View::setScalar()
713    */
714   template <typename ScalarType>
createViewScalar(const std::string & path,ScalarType value)715   View* createViewScalar(const std::string& path, ScalarType value)
716   {
717     View* view = createView(path);
718     if(view != nullptr)
719     {
720       view->setScalar(value);
721     }
722 
723     return view;
724   }
725 
726   /*!
727    * \brief Create View object with given name or path in this Group
728    * set its data to given string.
729    *
730    * This is equivalent to: createView(name)->setString(value);
731    *
732    * If given data type object is empty, data will not be allocated.
733    *
734    * \return pointer to new View object or nullptr if one is not created.
735    *
736    * \sa View::setString()
737    */
738   View* createViewString(const std::string& path, const std::string& value);
739 
740   //@}
741 
742   //@{
743   //!  @name View destruction methods.
744   //!
745   //! Each of these methods is a no-op if the specified View does not exist.
746 
747   /*!
748    * \brief Destroy View with given name or path owned by this Group, but leave
749    * its data intect.
750    */
751   void destroyView(const std::string& path);
752 
753   /*!
754    * \brief Destroy View with given index owned by this Group, but leave
755    * its data intect.
756    */
757   void destroyView(IndexType idx);
758 
759   /*!
760    * \brief Destroy all Views owned by this Group, but leave all their
761    *        data intact.
762    */
763   void destroyViews();
764 
765   /*!
766    * \brief Destroy View with given name or path owned by this Group and
767    * deallocate
768    * its data if it's the only View associated with that data.
769    */
770   void destroyViewAndData(const std::string& path);
771 
772   /*!
773    * \brief Destroy View with given index owned by this Group and deallocate
774    * its data if it's the only View associated with that data.
775    */
776   void destroyViewAndData(IndexType idx);
777 
778   /*!
779    * \brief Destroy all Views owned by this Group and deallocate
780    * data for each View when it's the only View associated with that data.
781    */
782   void destroyViewsAndData();
783 
784   //@}
785 
786   //@{
787   //!  @name View move and copy methods.
788 
789   /*!
790    * \brief Remove given View object from its owning Group and move it
791    *        to this Group.
792    *
793    * If given View pointer is null or Group already has a View with
794    * same name as given View, method is a no-op.
795    *
796    * \return pointer to given argument View object or nullptr if View
797    * is not moved into this Group.
798    */
799   View* moveView(View* view);
800 
801   /*!
802    * \brief Create a copy of given View object and add it to this Group.
803    *
804    * Note that View copying is a "shallow" copy; the data associated with
805    * the View is not copied. The new View object is associated with
806    * the same data as the original.
807    *
808    * If given Group pointer is null or Group already has a child Group with
809    * same name as given Group, method is a no-op.
810    *
811    * \return pointer to given argument Group object or nullptr if Group
812    * is not moved into this Group.
813    */
814   View* copyView(View* view);
815 
816   //@}
817 
818   //@{
819   //!  @name Child Group query methods.
820 
821   /*!
822    * \brief Return true if this Group has a descendant Group with given
823    * name or path; else false.
824    */
825   bool hasGroup(const std::string& path) const;
826 
827   /*!
828    * \brief Return true if this Group has a child Group with given
829    * name; else false.
830    */
831   bool hasChildGroup(const std::string& name) const;
832 
833   /*!
834    * \brief Return true if Group has an immediate child Group
835    *        with given index; else false.
836    */
837   bool hasGroup(IndexType idx) const;
838 
839   /*!
840    * \brief Return the index of immediate child Group with given name.
841    *
842    *        If no such child Group exists, return sidre::InvalidIndex;
843    */
844   IndexType getGroupIndex(const std::string& name) const;
845 
846   /*!
847    * \brief Return the name of immediate child Group with given index.
848    *
849    *        If no such child Group exists, return sidre::InvalidName.
850    */
851   const std::string& getGroupName(IndexType idx) const;
852 
853   //@}
854 
855   //@{
856   //!  @name Group access and iteration methods.
857 
858   /*!
859    * \brief Return pointer to non-const child Group with given name or path.
860    *
861    * This method requires that all Groups in the path exist if a path is given.
862    *
863    * If no such Group exists, nullptr is returned.
864    */
865   Group* getGroup(const std::string& path);
866 
867   /*!
868    * \brief Return pointer to const child Group with given name or path.
869    *
870    * This method requires that all Groups in the path exist if a path is given.
871    *
872    * If no such Group exists, nullptr is returned.
873    */
874   Group const* getGroup(const std::string& path) const;
875 
876   /*!
877    * \brief Return pointer to non-const immediate child Group with given index.
878    *
879    * If no such Group exists, nullptr is returned.
880    */
881   Group* getGroup(IndexType idx);
882 
883   /*!
884    * \brief Return pointer to const immediate child Group with given index.
885    *
886    * If no such Group exists, nullptr is returned.
887    */
888   const Group* getGroup(IndexType idx) const;
889 
890   //@}
891 
892   //@{
893   //!  @name Group iteration methods.
894   //!
895   //! Using these methods, a code can get the first Group index and each
896   //! succeeding index.  This allows Group iteration using the same
897   //! constructs in C++, C, and Fortran.  Example:
898   //!      for (sidre::IndexType idx = grp->getFirstValidGroupIndex();
899   //!           sidre::indexIsValid(idx);
900   //!           idx = grp->getNextValidGroupIndex(idx))
901   //!      {
902   //!          Group * group = grp->getGroup(idx);
903   //!
904   //!          /// code here using group
905   //!      }
906 
907   /*!
908    * \brief Return first valid child Group index (i.e., smallest
909    *        index over all child Groups).
910    *
911    * sidre::InvalidIndex is returned if Group has no child Groups.
912    *
913    * \sa axom::sidre::indexIsValid()
914    */
915   IndexType getFirstValidGroupIndex() const;
916 
917   /*!
918    * \brief Return next valid child Group index after given index
919    *        (i.e., smallest index over all child Group indices larger
920    *        than given one).
921    *
922    * sidre::InvalidIndex is returned if there is no valid index greater
923    * than given one.
924    *
925    * \sa axom::sidre::indexIsValid()
926    */
927   IndexType getNextValidGroupIndex(IndexType idx) const;
928 
929   //@}
930 
931   //@{
932   //!  @name Child Group creation and destruction methods.
933 
934   /*!
935    * \brief Create a child Group within this Group with given name or path.
936    *
937    * If name is an empty string or Group already has a child Group with
938    * given name or path, method is a no-op.
939    *
940    * The optional is_list argument is used to determine if the created
941    * child Group will hold items in list format.
942    *
943    * \return pointer to created Group object or nullptr if new
944    * Group is not created.
945    */
946   Group* createGroup(const std::string& path, bool is_list = false);
947 
948   /*
949    * \brief Create a child Group within this Group with no name.
950    *
951    * This is intended only to be called when this Group holds items in list
952    * format.  If this Group does not use list format, this method is a
953    * no-op.
954    *
955    * \return pointer to created Group object or nullptr if new Group is
956    * not created.
957    */
958   Group* createUnnamedGroup(bool is_list = false);
959 
960   /*!
961    * \brief Destroy child Group in this Group with given name or path.
962    *
963    * If no such Group exists, method is a no-op.
964    */
965   void destroyGroup(const std::string& path);
966 
967   /*!
968    * \brief Destroy child Group within this Group with given index.
969    *
970    * If no such Group exists, method is a no-op.
971    */
972   void destroyGroup(IndexType idx);
973 
974   /*!
975    * \brief Destroy all child Groups in this Group.
976    *
977    * Note that this will recursively destroy entire Group sub-tree below
978    * this Group.
979    */
980   void destroyGroups();
981 
982   //@}
983 
984   //@{
985   //!  @name Group move and copy methods.
986 
987   /*!
988    * \brief Remove given Group object from its parent Group and make it
989    *        a child of this Group.
990    *
991    * If given Group pointer is null or Group already has a child Group with
992    * same name as given Group, method is a no-op.
993    *
994    * \return pointer to given argument Group object or nullptr if Group
995    * is not moved into this Group.
996    */
997   Group* moveGroup(Group* group);
998 
999   /*!
1000    * \brief Create a copy of Group hierarchy rooted at given Group and make it
1001    *        a child of this Group.
1002    *
1003    * Note that all Views in the Group hierarchy are copied as well.
1004    *
1005    * Note that Group copying is a "shallow" copy; the data associated
1006    * with Views in a Group are not copied. In particular, the new Group
1007    * hierarchy and all its Views is associated with the same data as the
1008    * given Group.
1009    *
1010    * If given Group pointer is null or Group already has a child Group with
1011    * same name as given Group, method is a no-op.
1012    *
1013    * \return pointer to given argument Group object or nullptr if Group
1014    * is not moved into this Group.
1015    */
1016   Group* copyGroup(Group* group);
1017 
1018   //@}
1019 
1020   //@{
1021   //!  @name Group print methods.
1022 
1023   /*!
1024    * \brief Print JSON description of data Group to stdout.
1025    *
1026    * Note that this will recursively print entire Group sub-tree
1027    * starting at this Group object.
1028    */
1029   void print() const;
1030 
1031   /*!
1032    * \brief Print JSON description of data Group to an ostream.
1033    *
1034    * Note that this will recursively print entire Group sub-tree
1035    * starting at this Group object.
1036    */
1037   void print(std::ostream& os) const;
1038 
1039   /*!
1040    * \brief Print given number of levels of Group sub-tree
1041    *        starting at this Group object to an output stream.
1042    */
1043   void printTree(const int nlevels, std::ostream& os) const;
1044 
1045   //@}
1046 
1047   /*!
1048    * \brief Copy description of Group hierarchy rooted at this Group to
1049    * given Conduit node.
1050    *
1051    * The description includes Views of this Group and all of its children
1052    * recursively.
1053    */
1054   void copyToConduitNode(Node& n) const;
1055 
1056   /*!
1057    * \brief Copy data Group native layout to given Conduit node.
1058    *
1059    * The native layout is a Conduit Node hierarchy that maps the Conduit Node
1060    * data
1061    * externally to the Sidre View data so that it can be filled in from the data
1062    * in the file (independent of file format) and can be accessed as a Conduit
1063    * tree.
1064    *
1065    * \return True if the Group or any of its children were added to the Node,
1066    * false otherwise.
1067    *
1068    */
1069   bool createNativeLayout(Node& n, const Attribute* attr = nullptr) const;
1070 
1071   /*!
1072    * \brief Copy data Group external layout to given Conduit node.
1073    *
1074    * The external layout is a Conduit Node hierarchy that maps the Conduit Node
1075    * data externally to the Sidre View data so that it can be filled in from the
1076    * data in the file (independent of file format) and can be accessed as a
1077    * Conduit tree.
1078    *
1079    * Only the Views which have external data are added to the node.
1080    *
1081    * \return True if the Group or any of its children have an external
1082    *  View, false otherwise.
1083    */
1084   bool createExternalLayout(Node& n, const Attribute* attr = nullptr) const;
1085 
1086   /*!
1087    * \brief Return true if this Group is equivalent to given Group; else false.
1088    *
1089    * Two Groups are equivalent if they are the root Groups of identical
1090    * Group hierarchy structures with the same names for all Views and
1091    * Groups in the hierarchy, and the Views are also equivalent.
1092    *
1093    * \param other     The group to compare with
1094    * \param checkName If true (default), groups must have the same name
1095    *                  to be equivalent.  If false, disregard the name.
1096    *
1097    * \sa View::isEquivalentTo()
1098    */
1099   bool isEquivalentTo(const Group* other, bool checkName = true) const;
1100 
1101   /*!
1102    * \brief Return true if this Group holds items in map format.
1103    */
isUsingMap() const1104   bool isUsingMap() const { return !m_is_list; }
1105 
1106   /*!
1107    * \brief Return true if this Group holds items in list format.
1108    */
isUsingList() const1109   bool isUsingList() const { return m_is_list; }
1110 
1111   //@{
1112   /*!
1113  * @name    Group I/O methods
1114  *   These methods save and load Group trees to and from files.
1115  *   This includes the views and buffers used in by groups in the tree.
1116  *   We provide several "protocol" options:
1117  *
1118  *   protocols:
1119  *    sidre_hdf5 (default when Axom is configured with hdf5)
1120  *    sidre_conduit_json (default otherwise)
1121  *    sidre_json
1122  *
1123  *    conduit_hdf5
1124  *    conduit_bin
1125  *    conduit_json
1126  *    json
1127  *
1128  *   \note The sidre_hdf5 and conduit_hdf5 protocols are only available
1129  *   when Axom is configured with hdf5.
1130  *
1131  *   There are two overloaded versions for each of save, load, and
1132  *   loadExternalData.  The first of each takes a file path and is intended
1133  *   for use in a serial context and can be called directly using any
1134  *   of the supported protocols.  The second takes an hdf5 handle that
1135  *   has previously been created by the calling code.  These mainly exist
1136  *   to handle parallel I/O calls from the SPIO component.  They can only
1137  *   take the sidre_hdf5 or conduit_hdf5 protocols.
1138  *
1139  *   \note The hdf5 overloads are only available when Axom is configured
1140  *   with hdf5.
1141  */
1142 
1143   /*!
1144    * \brief Save the Group to a file.
1145    *
1146    *  Saves the tree starting at this Group and the Buffers used by the Views
1147    *  in this tree.
1148    *
1149    *  If attr is a null pointer, dump all Views.  Otherwise, only dump Views
1150    *  which have the Attribute set.
1151    *
1152    * \param path      file path
1153    * \param protocol  I/O protocol
1154    * \param attr      Save Views that have Attribute set.
1155    */
1156   void save(const std::string& path,
1157             const std::string& protocol = SIDRE_DEFAULT_PROTOCOL,
1158             const Attribute* attr = nullptr) const;
1159 
1160   /*!
1161    * \brief Load a Group hierarchy from a file into this Group
1162    *
1163    * This method instantiates the Group hierarchy and its Views stored
1164    * in the file under this Group.  The name of this Group is not
1165    * changed.
1166    *
1167    * If preserve_contents is true, then the names of the children held by the
1168    * Node cannot be the same as the names of the children already held by this
1169    * Group.  If there is a naming conflict, an error will occur.
1170    *
1171    * \param path     file path
1172    * \param protocol I/O protocol
1173    * \param preserve_contents  If true, any child Groups and Views held by
1174    *                           this Group remain in place.  If false, all
1175    *                           child Groups and Views are destroyed before
1176    *                           loading data from the file.
1177    */
1178   void load(const std::string& path,
1179             const std::string& protocol = SIDRE_DEFAULT_PROTOCOL,
1180             bool preserve_contents = false);
1181 
1182   /*!
1183    * \brief Load a Group hierarchy from a file into this Group, reporting
1184    *        the Group name stored in the file
1185    *
1186    * This method instantiates the Group hierarchy and its Views stored
1187    * in the file under this Group.  The name of this Group is not
1188    * changed.  The name of the group stored in the file is returned in
1189    * the output parameter name_from_file.  This can be used to rename the
1190    * group in a subsequent call.
1191    *
1192    * If preserve_contents is true, then the names of the children held by the
1193    * Node cannot be the same as the names of the children already held by this
1194    * Group.  If there is a naming conflict, an error will occur.
1195    *
1196    * \param [in]  path     file path to load
1197    * \param [in]  protocol I/O protocol to use
1198    * \param [in]  preserve_contents  If true, any child Groups and Views
1199    *                           held by this Group remain in place.
1200    *                           If false, all child Groups and Views are
1201    *                           destroyed before loading data from the file.
1202    * \param [out] name_from_file    Group name stored in the file
1203    */
1204   void load(const std::string& path,
1205             const std::string& protocol,
1206             bool preserve_contents,
1207             std::string& name_from_file);
1208 
1209   /*!
1210    * \brief Create a child Group and load a Group hierarchy from file
1211    *        into the new Group.
1212    *
1213    * This is a convenience routine for the following sequence:
1214    * - create a group with name or path group_name
1215    * - load a Group hierarchy from a file into the newly-created Group
1216    * - return the newly created Group, or nullptr if creation failed
1217    * - out-parameters return:
1218    *   - the group name from the file
1219    *   - a flag indicating success reading the file
1220    *
1221    * As with the createGroup() method, if group_name is empty or there
1222    * already exists a child Group with that name or path, the child Group
1223    * will not be created and this method will return nullptr.
1224    *
1225    * As with the load() method, after calling createGroupAndLoad() a host
1226    * code may choose to rename the newly-created Group with the string
1227    * returned in group_name.
1228    *
1229    * \param [in,out] group_name    In: name for the new group.
1230    *                               Out: the group name stored in the file.
1231    * \param [in]     path          file path
1232    * \param [in]     protocol      I/O protocol
1233    * \param [out]    load_success  Report success of the load operation
1234    *
1235    * \return pointer to created Group object or nullptr if new
1236    *         Group is not created.
1237    */
1238   Group* createGroupAndLoad(std::string& group_name,
1239                             const std::string& path,
1240                             const std::string& protocol,
1241                             bool& load_success);
1242 
1243   /*!
1244    * \brief Load data into the Group's external views from a file.
1245    *
1246    * No protocol argument is needed, as this only is used with the sidre_hdf5
1247    * protocol.
1248    *
1249    * \param path      file path
1250    */
1251   void loadExternalData(const std::string& path);
1252 
1253 #ifdef AXOM_USE_HDF5
1254 
1255   /*!
1256    * \brief Save the Group to an hdf5 handle.
1257    *
1258    *  If attr is nullptr, dump all Views.  Otherwise, only dump Views
1259    *  which have the Attribute set.
1260    *
1261    * \param h5_id      hdf5 handle
1262    * \param protocol   I/O protocol sidre_hdf5 or conduit_hdf5
1263    * \param attr       Save Views that have Attribute set.
1264    */
1265   void save(const hid_t& h5_id,
1266             const std::string& protocol = SIDRE_DEFAULT_PROTOCOL,
1267             const Attribute* attr = nullptr) const;
1268 
1269   /*!
1270    * \brief Load the Group from an hdf5 handle.
1271    *
1272    * If preserve_contents is true, then the names of the children held by the
1273    * Node cannot be the same as the names of the children already held by this
1274    * Group.  If there is a naming conflict, an error will occur.
1275    *
1276    * \param h5_id      hdf5 handle
1277    * \param protocol   I/O protocol sidre_hdf5 or conduit_hdf5
1278    * \param preserve_contents  If true, any child Groups and Views held by
1279    *                           this Group remain in place.  If false, all
1280    *                           child Groups and Views are destroyed before
1281    *                           loading data from the file.
1282    */
1283   void load(const hid_t& h5_id,
1284             const std::string& protocol = SIDRE_DEFAULT_PROTOCOL,
1285             bool preserve_contents = false);
1286 
1287   /*!
1288    * \brief Load the Group from an hdf5 handle.
1289    *
1290    * If preserve_contents is true, then the names of the children held by the
1291    * Node cannot be the same as the names of the children already held by this
1292    * Group.  If there is a naming conflict, an error will occur.
1293    *
1294    * \param [in]  h5_id      hdf5 handle
1295    * \param [in]  protocol   I/O protocol sidre_hdf5 or conduit_hdf5
1296    * \param [in]  preserve_contents  If true, any child Groups and Views held by
1297    *                           this Group remain in place.  If false, all
1298    *                           child Groups and Views are destroyed before
1299    *                           loading data from the file.
1300    * \param [out] name_from_file    Group name stored in the file
1301    */
1302   void load(const hid_t& h5_id,
1303             const std::string& protocol,
1304             bool preserve_contents,
1305             std::string& name_from_file);
1306 
1307   /*!
1308    * \brief Load data into the Group's external views from a hdf5 handle.
1309    *
1310    * No protocol argument is needed, as this only is used with the sidre_hdf5
1311    * protocol.
1312    *
1313    * \param h5_id      hdf5 handle
1314    */
1315   void loadExternalData(const hid_t& h5_id);
1316 
1317 #endif /* AXOM_USE_HDF5 */
1318 
1319   //@}
1320 
1321   /*!
1322    * \brief Change the name of this Group.
1323    *
1324    * The name of this group is changed to the new name.  If this group
1325    * has a parent group, the name for this group held by the parent is
1326    * also changed.
1327    *
1328    * Warnings will occur and the name will not be changed under these
1329    * conditions:  If the new name is an empty string, if the new name
1330    * contains a path delimiter (usually '/'), or if the new name is
1331    * identical to a name that is already held by the parent for another
1332    * Group or View object.
1333    *
1334    * It is possible to rename the root Group, but a code cannot
1335    * subsequently rename root Group back to its original empty string
1336    * name.
1337    *
1338    * \param new_name    The new name for this group.
1339    *
1340    * \return            Success or failure of rename.
1341    */
1342   bool rename(const std::string& new_name);
1343 
1344   /*!
1345    * \brief Import data from a conduit Node into a Group
1346    *
1347    * This imports the hierarchy from the Node into a Sidre Group with the
1348    * same tree structure.
1349    *
1350    * This does not support conduit's list datatype.  If the Node contains a
1351    * list any where in its tree, the list and any child Nodes descending from
1352    * the list will not be imported.  A warning will occur and an unsuccessful
1353    * return value will be returned.
1354    *
1355    * If preserve_contents is true, then the names of the children held by the
1356    * Node cannot be the same as the names of the children already held by this
1357    * Group.  If there is a naming conflict, an error will occur.
1358    *
1359    * \param node               A conduit Node containing hierarchical data.
1360    * \param preserve_contents  If true, any child Groups and Views held by
1361    *                           this Group remain in place.  If false, all
1362    *                           child Groups and Views are destroyed before
1363    *                           importing data from the Node.
1364    *
1365    * \return                   true for success, false if the full conduit
1366    *                           tree is not succesfully imported.
1367    */
1368   bool importConduitTree(const conduit::Node& node,
1369                          bool preserve_contents = false);
1370 
1371   /*!
1372    * \brief Import data from a conduit Node into a Group without copying arrays
1373    *
1374    * This differs from the importConduitTree in that it does not copy any
1375    * data held by the Node as an array.  Instead it imports the existing
1376    * pointer to the array as an external pointer.
1377    *
1378    * This imports the hierarchy from the Node into a Sidre Group with the
1379    * same tree structure.
1380    *
1381    * This does not support conduit's list datatype.  If the Node contains a
1382    * list any where in its tree, the list and any child Nodes descending from
1383    * the list will not be imported.  A warning will occur and an unsuccessful
1384    * return value will be returned.
1385    *
1386    * If preserve_contents is true, then the names of the children held by the
1387    * Node cannot be the same as the names of the children already held by this
1388    * Group.  If there is a naming conflict, an error will occur.
1389    *
1390    * \param node               A conduit Node containing hierarchical data.
1391    * \param preserve_contents  If true, any child Groups and Views held by
1392    *                           this Group remain in place.  If false, all
1393    *                           child Groups and Views are destroyed before
1394    *                           importing data from the Node.
1395    *
1396    * \return                   true for success, false if the full conduit
1397    *                           tree is not succesfully imported.
1398    */
1399   bool importConduitTreeExternal(conduit::Node& node,
1400                                  bool preserve_contents = false);
1401 
1402 private:
1403   DISABLE_DEFAULT_CTOR(Group);
1404   DISABLE_COPY_AND_ASSIGNMENT(Group);
1405   DISABLE_MOVE_AND_ASSIGNMENT(Group);
1406 
1407   //@{
1408   //!  @name Private Group ctors and dtors
1409   //!        (callable only by DataStore and Group methods).
1410 
1411   /*!
1412    *  \brief Private ctor that creates a Group with given name
1413    *         in the given DataStore.
1414    *
1415    *  attachGroup must be called on a newly created Group to insert it
1416    *  into the hierarchy. The root group is an exception to this rule.
1417    *
1418    *  The boolean argument is_list, if true, allows the Group to hold its
1419    *  child items in list format, which allows those items to have empty
1420    *  strings for names.  If not in list format, all items must have unique
1421    *  non-empty strings for names.
1422    */
1423   Group(const std::string& name, DataStore* datastore, bool is_list);
1424 
1425   /*!
1426    * \brief Destructor destroys all Views and child Groups.
1427    */
1428   ~Group();
1429 
1430   //@}
1431 
1432   //@{
1433   //!  @name View attach and detach methods.
1434 
1435   /*!
1436    * \brief Attach View object to this Group.
1437    */
1438   View* attachView(View* view);
1439 
1440   /*!
1441    * \brief Detach View object from this Group.
1442    */
detachView(const View * view)1443   View* detachView(const View* view) { return detachView(view->getName()); }
1444 
1445   /*!
1446    * \brief Detach View with given name from this Group.
1447    */
1448   View* detachView(const std::string& name);
1449 
1450   /*!
1451    * \brief Detach View with given index from this Group.
1452    */
1453   View* detachView(IndexType idx);
1454 
1455   //@}
1456 
1457   //@{
1458   //!  @name Group attach and detach methods.
1459 
1460   /*!
1461    * \brief Attach Group object to this Group.
1462    */
1463   Group* attachGroup(Group* view);
1464 
1465   /*!
1466    * \brief Detach Child Group with given name from this Group.
1467    */
1468   Group* detachGroup(const std::string& name);
1469 
1470   /*!
1471    * \brief Detach Child Group with given index from this Group.
1472    */
1473   Group* detachGroup(IndexType idx);
1474 
1475   //@}
1476 
1477   //@{
1478   //!  @name Private Group View manipulation methods.
1479 
1480   /*!
1481    * \brief Destroy View and its data if its data is not shared with any
1482    * other View.
1483    *
1484    * Data will not be destroyed as long as a View still exists that
1485    * references it.
1486    *
1487    * \attention this method assumes View is owned by this Group.
1488    */
1489   void destroyViewAndData(View* view);
1490 
1491   //@}
1492 
1493   //@{
1494   //!  @name Private Group methods for interacting with Conduit Nodes.
1495 
1496   /*!
1497    * \brief Private method to copy Group to Conduit Node.
1498    *
1499    * Note: This is for the "sidre_{zzz}" protocols.
1500    *
1501    * \return True if the group or any of its children have saved Views,
1502    * false otherwise.
1503    */
1504   bool exportTo(conduit::Node& result, const Attribute* attr) const;
1505 
1506   /*!
1507    * \brief Private method to copy Group to Conduit Node.
1508    *
1509    * \param buffer_indices Used to track what Buffers are referenced
1510    * by the Views in this Group and Groups in the sub-tree below it.
1511    *
1512    * \return True if the group or any of its children have saved Views,
1513    * false otherwise.
1514    */
1515   bool exportTo(conduit::Node& data_holder,
1516                 const Attribute* attr,
1517                 std::set<IndexType>& buffer_indices) const;
1518 
1519   /*!
1520    * \brief Private method to build a Group hierarchy from Conduit Node.
1521    *
1522    * Note: This is for the "sidre_{zzz}" protocols.
1523    */
1524   void importFrom(conduit::Node& node, bool preserve_contents = false);
1525 
1526   /*!
1527    * \brief Private method to copy Group from Conduit Node.
1528    *
1529    * Map of Buffer indices tracks old Buffer ids in the file to the
1530    * new Buffer ids in the datastore.  Buffer ids are not guaranteed
1531    * to remain the same when a tree is restored.
1532    *
1533    */
1534   void importFrom(conduit::Node& node,
1535                   const std::map<IndexType, IndexType>& buffer_id_map);
1536 
1537   //@}
1538 
1539   /*!
1540    * \brief Private method that returns the Group that is the next-to-last
1541    * entry in a slash-delimited ("/") path string.
1542    *
1543    * The string before the last "/" character, if there is one, is the
1544    * next-to-last path entry. In this case, the return value is that Group
1545    * in the path.
1546    *
1547    * If there is no "/" in the given path, the entire string is considered
1548    * the next-to-last path entry. In this case, the return value is this
1549    * Group.
1550    *
1551    * The path argument is modified while walking the path. Its value when
1552    * the method returns is the last entry in the path, either the string
1553    * following the last "/" in the input (if there is one) or the entire
1554    * input path string if it contains no "/".
1555    */
1556   Group* walkPath(std::string& path, bool create_groups_in_path);
1557 
1558   /*!
1559    * \brief Const private method that returns the Group that is the
1560    * next-to-last entry in a delimited path string.
1561    *
1562    * The path argument is modified while walking the path. Its value when
1563    * the method returns is the last entry in the path, either the string
1564    * following the last "/" in the input (if there is one) or the entire
1565    * input path string if it contains no "/".
1566    */
1567   const Group* walkPath(std::string& path) const;
1568 
1569   /*!
1570    * \brief Private method. If allocatorID is a valid allocator ID then return
1571    *  it. Otherwise return the ID of the default allocator of the owning group.
1572    */
1573   int getValidAllocatorID(int allocatorID);
1574 
1575   /// Name of this Group object.
1576   std::string m_name;
1577 
1578   /// Index of this Group object within m_parent.
1579   IndexType m_index;
1580 
1581   /// Parent Group of this Group object.
1582   Group* m_parent;
1583 
1584   /// This Group object lives in the tree of this DataStore object.
1585   DataStore* m_datastore;
1586 
1587   /// This identifies whether this Group holds items in list format.
1588   bool m_is_list;
1589 
1590   /// Character used to denote a path string passed to get/create calls.
1591   AXOM_EXPORT static const char s_path_delimiter;
1592 
1593   ///////////////////////////////////////////////////////////////////
1594   //
1595   using ViewCollection = ItemCollection<View>;
1596   //
1597   using GroupCollection = ItemCollection<Group>;
1598   ///////////////////////////////////////////////////////////////////
1599 
1600   /// Collection of Views
1601   ViewCollection* m_view_coll;
1602 
1603   /// Collection of child Groups
1604   GroupCollection* m_group_coll;
1605 
1606 #ifdef AXOM_USE_UMPIRE
1607   int m_default_allocator_id;
1608 #endif
1609 };
1610 
1611 } /* end namespace sidre */
1612 } /* end namespace axom */
1613 
1614 #endif /* SIDRE_GROUP_HPP_ */
1615