1 /**
2  *  \file RMF/FileConstHandle.h
3  *  \brief Handle read/write of Model data from/to files.
4  *
5  *  Copyright 2007-2021 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef RMF_FILE_CONST_HANDLE_H
10 #define RMF_FILE_CONST_HANDLE_H
11 
12 #include <boost/current_function.hpp>
13 #include <boost/functional/hash.hpp>
14 #include <boost/shared_ptr.hpp>
15 #include <iosfwd>
16 #include <limits>
17 #include <string>
18 #include <vector>
19 
20 #include "BufferHandle.h"
21 #include "NodeConstHandle.h"
22 #include "RMF/ID.h"
23 #include "RMF/config.h"
24 #include "RMF/constants.h"
25 #include "RMF/enums.h"
26 #include "RMF/exceptions.h"
27 #include "RMF/infrastructure_macros.h"
28 #include "RMF/internal/SharedData.h"
29 #include "RMF/internal/errors.h"
30 #include "RMF/internal/shared_data_ranges.h"
31 #include "internal/SharedData.h"
32 #include "internal/shared_data_ranges.h"
33 #include "types.h"
34 
35 namespace RMF {
36 class BufferConstHandle;
37 class FileConstHandle;
38 }  // namespace RMF
39 
40 RMF_ENABLE_WARNINGS
41 
42 #define RMF_FILE_CATCH(extra_info)                                        \
43   catch (Exception& e) {                                                  \
44     RMF_RETHROW(                                                          \
45         File(get_path()) << Frame(get_current_frame())                    \
46                          << Operation(BOOST_CURRENT_FUNCTION) extra_info, \
47         e);                                                               \
48   }
49 
50 #ifndef SWIG
51 #define RMF_HDF5_ROOT_CONST_KEY_TYPE_METHODS(Traits, UCName)
52 #else
53 #define RMF_HDF5_ROOT_CONST_KEY_TYPE_METHODS(Traits, UCName)        \
54   UCName##Key get_key(Category category_id, std::string nm,         \
55                       UCName##Tag) const;                           \
56   std::string get_name(UCName##Key k) const;                        \
57   Category get_category(UCName##Key k) const;                       \
58   /** This returns all the keys that are used in the current frame. \
59       Other frames may have different ones.*/                       \
60   UCName##Key##s get_keys(Category category_id, UCName##Tag);
61 #endif
62 
63 namespace RMF {
64 
65 class FileConstHandle;
66 class FileHandle;
67 class NodeConstHandle;
68 
69 //! Pass a list of them
70 typedef std::vector<FileConstHandle> FileConstHandles;
71 
72 //! A handle for a read-only RMF file
73 /** Use this handle to perform operations relevant to the
74     whole RMF hierarchy as well as to start traversal of the
75     hierarchy.
76     \see open_rmf_file_read_only
77  */
78 class RMFEXPORT FileConstHandle {
79   void gather_ids(NodeConstHandle n, Ints& ids, std::vector<std::string>& paths,
80                   std::string path) const;
81 #ifndef SWIG
82   friend class RMFEXPORT NodeConstHandle;
83   friend RMFEXPORT void clone_file_info(FileConstHandle, FileHandle);
84   friend RMFEXPORT void clone_hierarchy(FileConstHandle, FileHandle);
85   friend RMFEXPORT void clone_static_frame(FileConstHandle, FileHandle);
86   friend RMFEXPORT void clone_loaded_frame(FileConstHandle, FileHandle);
87   friend RMFEXPORT bool get_equal_current_values(FileConstHandle,
88                                                  FileConstHandle);
89   friend RMFEXPORT bool get_equal_static_values(FileConstHandle,
90                                                 FileConstHandle);
91 #endif
compare(const FileConstHandle & o)92   int compare(const FileConstHandle& o) const {
93     if (get_name() < o.get_name())
94       return -1;
95     else if (get_name() > o.get_name())
96       return 1;
97     else
98       return 0;
99   }
100 
101  protected:
102   boost::shared_ptr<internal::SharedData> shared_;
103 
104  public:
105   RMF_COMPARISONS(FileConstHandle);
106   RMF_HASHABLE(FileConstHandle, return boost::hash_value(get_name()););
107   RMF_SHOWABLE(FileConstHandle, get_name());
108   //! Empty root handle, no open file.
FileConstHandle()109   FileConstHandle() {}
110 #if !defined(RMF_DOXYGEN) && !defined(SWIG)
111   FileConstHandle(boost::shared_ptr<internal::SharedData> shared);
112 #endif
113 
114   //! Return the root of the hierarchy
get_root_node()115   NodeConstHandle get_root_node() const {
116     return NodeConstHandle(NodeID(0), shared_);
117   }
118 
get_name()119   std::string get_name() const { return shared_->get_file_name(); }
120 
get_path()121   std::string get_path() const { return shared_->get_file_path(); }
122 
123   /** \name Methods for manipulating keys
124       When using C++ it is most convenient to specify types
125       when adding and using keys through template arguments. For python
126       we provide non-template versions, below.
127       @{
128    */
129   /** Get an existing key that has the given name of the
130       given type or Key() if the key is not found.
131    */
132   template <class Tag>
get_key(Category category,std::string name)133   ID<Tag> get_key(Category category, std::string name) const {
134     try {
135       return shared_->get_key(category, name, Tag());
136     }
137     RMF_FILE_CATCH(<< Category(get_name(category)) << Key(name));
138   }
139 
140   template <class Tag>
get_keys(Category category_id,const Strings & names)141   std::vector<ID<Tag> > get_keys(Category category_id,
142                                  const Strings& names) const {
143     try {
144       std::vector<ID<Tag> > ret(names.size());
145       for (unsigned int i = 0; i < names.size(); ++i) {
146         ret[i] = get_key<Tag>(category_id, names[i]);
147         if (ret[i] == ID<Tag>()) {
148           ret.clear();
149           return ret;
150         }
151       }
152       return ret;
153     }
154     RMF_FILE_CATCH(<< Category(get_name(category_id)));
155   }
156 
157   /** Get a list of all keys of the given type,
158    */
159   template <class Tag>
get_keys(Category category)160   std::vector<ID<Tag> > get_keys(Category category) const {
161     try {
162       if (category == Category()) return std::vector<ID<Tag> >();
163       return shared_->get_keys(category, Tag());
164     }
165     RMF_FILE_CATCH(<< Category(get_name(category)));
166   }
167 
168   template <class Tag>
get_key(Category category_id,std::string nm,Tag)169   ID<Tag> get_key(Category category_id, std::string nm, Tag) const {
170     return get_key<Tag>(category_id, nm);
171   }
172   template <class Tag>
get_name(ID<Tag> k)173   std::string get_name(ID<Tag> k) const {
174     try {
175       return shared_->get_name(k);
176     }
177     RMF_FILE_CATCH();
178   }
179   template <class Tag>
get_category(ID<Tag> k)180   Category get_category(ID<Tag> k) const {
181     return shared_->get_category(k);
182   }
183   /** This returns all the keys that are used in the current frame. \
184       Other frames may have different ones.*/
185   template <class Tag>
get_keys(Category category_id,Tag)186   std::vector<ID<Tag> > get_keys(Category category_id, Tag) {
187     return get_keys<Tag>(category_id);
188   }
189 
190   /** @} */
191 
192   /** The file always has a single frame that is currently active at any given
193       point.
194       @{
195    */
get_current_frame()196   FrameID get_current_frame() const { return shared_->get_loaded_frame(); }
get_type(FrameID fr)197   FrameType get_type(FrameID fr) const {
198     return shared_->get_frame_data(fr).type;
199   }
get_name(FrameID fr)200   std::string get_name(FrameID fr) const {
201     return shared_->get_frame_data(fr).name;
202   }
get_children(FrameID id)203   FrameIDs get_children(FrameID id) const {
204     const internal::FrameData& fd = shared_->get_frame_data(id);
205     return FrameIDs(fd.children.begin(), fd.children.end());
206   }
get_parents(FrameID id)207   FrameIDs get_parents(FrameID id) const {
208     const internal::FrameData& fd = shared_->get_frame_data(id);
209     return FrameIDs(fd.parents.begin(), fd.parents.end());
210   }
set_current_frame(FrameID frame)211   void set_current_frame(FrameID frame) const {
212     RMF_USAGE_CHECK(frame != FrameID(), "Invalid frame passed.");
213     RMF_USAGE_CHECK(frame != ALL_FRAMES,
214                     "Use set_static_value() and get_static_value() to "
215                     "manipulate the static frame.");
216     try {
217       shared_->set_loaded_frame(frame);
218     }
219     RMF_FILE_CATCH(<< Frame(frame));
220   }
221 
222   /** Return the number of frames in the file.
223    */
get_number_of_frames()224   unsigned int get_number_of_frames() const {
225     try {
226       return shared_->get_number_of_frames();
227     }
228     RMF_FILE_CATCH();
229   }
230 
231   /** Return the number of nodes in the file.
232    */
get_number_of_nodes()233   unsigned int get_number_of_nodes() const {
234     try {
235       return shared_->get_number_of_nodes();
236     }
237     RMF_FILE_CATCH();
238   }
239 
240   /** Return a string identifying the file type.
241   */
get_file_type()242   std::string get_file_type() const { return shared_->get_file_type(); }
243 
244   /** Get all the frames that are roots (aren't subframes). */
245   FrameIDs get_root_frames() const;
246 
247 #ifndef SWIG
get_frames()248   boost::iterator_range<internal::integer_iterator<FrameID> > get_frames()
249       const {
250     return internal::get_frames(shared_.get());
251   }
252 
get_node_ids()253   boost::iterator_range<internal::integer_iterator<NodeID> > get_node_ids()
254       const {
255     return internal::get_nodes(shared_.get());
256   }
257 #endif
258 
259   /** \name Non-template versions for python
260 
261       @{
262    */
263 
264   RMF_FOREACH_TYPE(RMF_HDF5_ROOT_CONST_KEY_TYPE_METHODS);
265 
266 /** @} */
267 #ifdef RMF_DOXYGEN
268   /** \name Python only
269       The following methods are only available in python.
270       @{
271    */
272   //! Return a list with all the keys from that category
273   PythonList get_keys(Category c) const;
274 /** @} */
275 #endif
276 #ifndef SWIG
277   /** Each node in the hierarchy can be associated with some arbitrary bit
278       of external data. Nodes can be extracted using
279       these bits of data.
280    */
281   template <class T>
get_node_from_association(const T & d)282   NodeConstHandle get_node_from_association(const T& d) const {
283     if (!shared_->get_has_associated_node(d)) {
284       return NodeConstHandle();
285     } else {
286       return NodeConstHandle(shared_->get_associated_node(d), shared_);
287     }
288   }
289 #else
290   NodeConstHandle get_node_from_association(void* v) const;
291 #endif
292   NodeConstHandle get_node(NodeID id) const;
293 
294   /** Along with the associations for nodes, arbitrary data can
295       be associated with the file in memory to aid in processing.
296    */
297   template <class T>
add_associated_data(int index,const T & t)298   void add_associated_data(int index, const T& t) {
299     shared_->set_user_data(index, t);
300   }
301   /** To get back the ith user data.*/
302   template <class T>
get_associated_data(int index)303   T get_associated_data(int index) {
304     return shared_->get_user_data<T>(index);
305   }
306 
307   /** To get back the ith user data.*/
get_has_associated_data(int index)308   bool get_has_associated_data(int index) {
309     return shared_->get_has_user_data(index);
310   }
311 
312   /** Each RMF structure has an associated description. This should
313       consist of unstructured text describing the contents of the RMF
314       data. Conventionally. this description can consist of multiple
315       paragraphs, each separated by a newline character and should end
316       in a newline.
317    */
318   std::string get_description() const;
319 
320   /** Each RMF structure has an associated field that the code that
321       produced the file can use to describe itself.
322    */
323   std::string get_producer() const;
324 
325   /** \name Key categories methods
326       Methods for managing the key categories in this RMF.
327       @{
328    */
get_category(std::string name)329   Category get_category(std::string name) {
330     try {
331       return shared_->get_category(name);
332     }
333     RMF_FILE_CATCH(<< Category(name));
334   }
335   /** This returns all the categories that are used in the current frame.
336       Other frames may have different ones.*/
get_categories()337   Categories get_categories() const {
338     try {
339       return shared_->get_categories();
340     }
341     RMF_FILE_CATCH();
342   }
get_name(Category kc)343   std::string get_name(Category kc) const {
344     try {
345       return shared_->get_name(kc);
346     }
347     RMF_FILE_CATCH();
348   }
349   /** @} */
350 
351   //! Reread the file.
352   /** \note This may invalidate various things (e.g. the number of nodes may
353       vary). Be careful.
354    */
355   void reload();
356 };
357 
358 //! Produce hash values for boost hash tables.
hash_value(const FileConstHandle & t)359 inline std::size_t hash_value(const FileConstHandle& t) {
360   return t.__hash__();
361 }
362 
363 /**
364    Open an RMF from a file system path in read-only mode.
365 
366    \param path the system path to the rmf file
367    \exception RMF::IOException couldn't open file, or unsupported file format
368  */
369 RMFEXPORT FileConstHandle open_rmf_file_read_only(std::string path);
370 
371 /**
372    Open an RMF from a buffer in read-only mode.
373 
374    \exception RMF::IOException couldn't open file, or unsupported file format
375  */
376 RMFEXPORT FileConstHandle open_rmf_buffer_read_only(BufferConstHandle buffer);
377 
378 /** \name Batch data access
379     These methods provide batch access to attribute data to try
380     to reduce the overhead of repeated function calls.
381 
382     The missing_value argument is a placeholder that can fill in
383     for values which are not found in the respective node.
384 
385     \note These methods are experimental and subject to change.
386     @{
387  */
388 RMFEXPORT Floats
389     get_values(const NodeConstHandles& nodes, FloatKey k,
390                Float missing_value = std::numeric_limits<float>::max());
391 /** @} */
392 
393 } /* namespace RMF */
394 
395 RMF_DISABLE_WARNINGS
396 
397 #endif /* RMF_FILE_CONST_HANDLE_H */
398