| /* |
| Open Asset Import Library (assimp) |
| ---------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the |
| following conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| ---------------------------------------------------------------------- |
| */ |
| |
| #ifndef INCLUDED_AI_STEPFILE_H |
| #define INCLUDED_AI_STEPFILE_H |
| |
| #include <bitset> |
| #include <memory> |
| #include <typeinfo> |
| #include <vector> |
| #include <map> |
| #include <set> |
| |
| #include "FBXDocument.h" //ObjectMap::value_type |
| #include <assimp/DefaultLogger.hpp> |
| |
| // |
| #if _MSC_VER > 1500 || (defined __GNUC___) |
| # define ASSIMP_STEP_USE_UNORDERED_MULTIMAP |
| #else |
| # define step_unordered_map map |
| # define step_unordered_multimap multimap |
| #endif |
| |
| #ifdef ASSIMP_STEP_USE_UNORDERED_MULTIMAP |
| # include <unordered_map> |
| # if _MSC_VER > 1600 |
| # define step_unordered_map unordered_map |
| # define step_unordered_multimap unordered_multimap |
| # else |
| # define step_unordered_map tr1::unordered_map |
| # define step_unordered_multimap tr1::unordered_multimap |
| # endif |
| #endif |
| |
| #include "LineSplitter.h" |
| |
| // uncomment this to have the loader evaluate all entities upon loading. |
| // this is intended as stress test - by default, entities are evaluated |
| // lazily and therefore not unless needed. |
| |
| //#define ASSIMP_IFC_TEST |
| |
| namespace Assimp { |
| |
| // ******************************************************************************** |
| // before things get complicated, this is the basic outline: |
| |
| |
| namespace STEP { |
| |
| namespace EXPRESS { |
| |
| // base data types known by EXPRESS schemata - any custom data types will derive one of those |
| class DataType; |
| class UNSET; /*: public DataType */ |
| class ISDERIVED; /*: public DataType */ |
| // class REAL; /*: public DataType */ |
| class ENUM; /*: public DataType */ |
| // class STRING; /*: public DataType */ |
| // class INTEGER; /*: public DataType */ |
| class ENTITY; /*: public DataType */ |
| class LIST; /*: public DataType */ |
| // class SELECT; /*: public DataType */ |
| |
| // a conversion schema is not exactly an EXPRESS schema, rather it |
| // is a list of pointers to conversion functions to build up the |
| // object tree from an input file. |
| class ConversionSchema; |
| } |
| |
| struct HeaderInfo; |
| class Object; |
| class LazyObject; |
| class DB; |
| |
| |
| typedef Object* (*ConvertObjectProc)(const DB& db, const EXPRESS::LIST& params); |
| } |
| |
| // ******************************************************************************** |
| |
| namespace STEP { |
| |
| // ------------------------------------------------------------------------------- |
| /** Exception class used by the STEP loading & parsing code. It is typically |
| * coupled with a line number. */ |
| // ------------------------------------------------------------------------------- |
| struct SyntaxError : DeadlyImportError { |
| enum { |
| LINE_NOT_SPECIFIED = 0xffffffffffffffffLL |
| }; |
| |
| SyntaxError (const std::string& s,uint64_t line = LINE_NOT_SPECIFIED); |
| }; |
| |
| |
| // ------------------------------------------------------------------------------- |
| /** Exception class used by the STEP loading & parsing code when a type |
| * error (i.e. an entity expects a string but receives a bool) occurs. |
| * It is typically coupled with both an entity id and a line number.*/ |
| // ------------------------------------------------------------------------------- |
| struct TypeError : DeadlyImportError |
| { |
| enum { |
| ENTITY_NOT_SPECIFIED = 0xffffffffffffffffLL |
| }; |
| |
| TypeError (const std::string& s,uint64_t entity = ENTITY_NOT_SPECIFIED, uint64_t line = SyntaxError::LINE_NOT_SPECIFIED); |
| }; |
| |
| |
| // hack to make a given member template-dependent |
| template <typename T, typename T2> |
| T2& Couple(T2& in) { |
| return in; |
| } |
| |
| |
| namespace EXPRESS { |
| |
| // ------------------------------------------------------------------------------- |
| //** Base class for all STEP data types */ |
| // ------------------------------------------------------------------------------- |
| class DataType |
| { |
| public: |
| |
| typedef std::shared_ptr<const DataType> Out; |
| |
| public: |
| |
| virtual ~DataType() { |
| } |
| |
| public: |
| |
| template <typename T> |
| const T& To() const { |
| return dynamic_cast<const T&>(*this); |
| } |
| |
| template <typename T> |
| T& To() { |
| return dynamic_cast<T&>(*this); |
| } |
| |
| |
| template <typename T> |
| const T* ToPtr() const { |
| return dynamic_cast<const T*>(this); |
| } |
| |
| template <typename T> |
| T* ToPtr() { |
| return dynamic_cast<T*>(this); |
| } |
| |
| // utilities to deal with SELECT entities, which currently lack automatic |
| // conversion support. |
| template <typename T> |
| const T& ResolveSelect(const DB& db) const { |
| return Couple<T>(db).MustGetObject(To<EXPRESS::ENTITY>())->template To<T>(); |
| } |
| |
| template <typename T> |
| const T* ResolveSelectPtr(const DB& db) const { |
| const EXPRESS::ENTITY* e = ToPtr<EXPRESS::ENTITY>(); |
| return e?Couple<T>(db).MustGetObject(*e)->template ToPtr<T>():(const T*)0; |
| } |
| |
| public: |
| |
| /** parse a variable from a string and set 'inout' to the character |
| * behind the last consumed character. An optional schema enables, |
| * if specified, automatic conversion of custom data types. |
| * |
| * @throw SyntaxError |
| */ |
| static std::shared_ptr<const EXPRESS::DataType> Parse(const char*& inout, |
| uint64_t line = SyntaxError::LINE_NOT_SPECIFIED, |
| const EXPRESS::ConversionSchema* schema = NULL); |
| |
| public: |
| }; |
| |
| typedef DataType SELECT; |
| typedef DataType LOGICAL; |
| |
| // ------------------------------------------------------------------------------- |
| /** Sentinel class to represent explicitly unset (optional) fields ($) */ |
| // ------------------------------------------------------------------------------- |
| class UNSET : public DataType |
| { |
| public: |
| private: |
| }; |
| |
| // ------------------------------------------------------------------------------- |
| /** Sentinel class to represent explicitly derived fields (*) */ |
| // ------------------------------------------------------------------------------- |
| class ISDERIVED : public DataType |
| { |
| public: |
| private: |
| }; |
| |
| // ------------------------------------------------------------------------------- |
| /** Shared implementation for some of the primitive data type, i.e. int, float */ |
| // ------------------------------------------------------------------------------- |
| template <typename T> |
| class PrimitiveDataType : public DataType |
| { |
| public: |
| |
| // This is the type that will cd ultimatively be used to |
| // expose this data type to the user. |
| typedef T Out; |
| |
| public: |
| |
| PrimitiveDataType() {} |
| PrimitiveDataType(const T& val) |
| : val(val) |
| {} |
| |
| PrimitiveDataType(const PrimitiveDataType& o) { |
| (*this) = o; |
| } |
| |
| |
| public: |
| |
| operator const T& () const { |
| return val; |
| } |
| |
| PrimitiveDataType& operator=(const PrimitiveDataType& o) { |
| val = o.val; |
| return *this; |
| } |
| |
| protected: |
| T val; |
| |
| }; |
| |
| typedef PrimitiveDataType<int64_t> INTEGER; |
| typedef PrimitiveDataType<double> REAL; |
| typedef PrimitiveDataType<double> NUMBER; |
| typedef PrimitiveDataType<std::string> STRING; |
| |
| |
| |
| // ------------------------------------------------------------------------------- |
| /** Generic base class for all enumerated types */ |
| // ------------------------------------------------------------------------------- |
| class ENUMERATION : public STRING |
| { |
| public: |
| |
| ENUMERATION (const std::string& val) |
| : STRING(val) |
| {} |
| |
| private: |
| }; |
| |
| typedef ENUMERATION BOOLEAN; |
| |
| // ------------------------------------------------------------------------------- |
| /** This is just a reference to an entity/object somewhere else */ |
| // ------------------------------------------------------------------------------- |
| class ENTITY : public PrimitiveDataType<uint64_t> |
| { |
| public: |
| |
| ENTITY(uint64_t val) |
| : PrimitiveDataType<uint64_t>(val) |
| { |
| ai_assert(val!=0); |
| } |
| |
| ENTITY() |
| : PrimitiveDataType<uint64_t>(TypeError::ENTITY_NOT_SPECIFIED) |
| { |
| } |
| |
| private: |
| }; |
| |
| // ------------------------------------------------------------------------------- |
| /** Wrap any STEP aggregate: LIST, SET, ... */ |
| // ------------------------------------------------------------------------------- |
| class LIST : public DataType |
| { |
| public: |
| |
| // access a particular list index, throw std::range_error for wrong indices |
| std::shared_ptr<const DataType> operator[] (size_t index) const { |
| return members[index]; |
| } |
| |
| size_t GetSize() const { |
| return members.size(); |
| } |
| |
| public: |
| |
| /** @see DaraType::Parse */ |
| static std::shared_ptr<const EXPRESS::LIST> Parse(const char*& inout, |
| uint64_t line = SyntaxError::LINE_NOT_SPECIFIED, |
| const EXPRESS::ConversionSchema* schema = NULL); |
| |
| |
| private: |
| typedef std::vector< std::shared_ptr<const DataType> > MemberList; |
| MemberList members; |
| }; |
| |
| |
| // ------------------------------------------------------------------------------- |
| /* Not exactly a full EXPRESS schema but rather a list of conversion functions |
| * to extract valid C++ objects out of a STEP file. Those conversion functions |
| * may, however, perform further schema validations. */ |
| // ------------------------------------------------------------------------------- |
| class ConversionSchema |
| { |
| |
| public: |
| |
| struct SchemaEntry { |
| SchemaEntry(const char* name,ConvertObjectProc func) |
| : name(name) |
| , func(func) |
| {} |
| |
| const char* name; |
| ConvertObjectProc func; |
| }; |
| |
| typedef std::map<std::string,ConvertObjectProc> ConverterMap; |
| |
| public: |
| |
| template <size_t N> |
| explicit ConversionSchema( const SchemaEntry (& schemas)[N]) { |
| *this = schemas; |
| } |
| |
| ConversionSchema() {} |
| |
| public: |
| |
| ConvertObjectProc GetConverterProc(const std::string& name) const { |
| ConverterMap::const_iterator it = converters.find(name); |
| return it == converters.end() ? NULL : (*it).second; |
| } |
| |
| |
| bool IsKnownToken(const std::string& name) const { |
| return converters.find(name) != converters.end(); |
| } |
| |
| const char* GetStaticStringForToken(const std::string& token) const { |
| ConverterMap::const_iterator it = converters.find(token); |
| return it == converters.end() ? NULL : (*it).first.c_str(); |
| } |
| |
| |
| template <size_t N> |
| const ConversionSchema& operator=( const SchemaEntry (& schemas)[N]) { |
| for(size_t i = 0; i < N; ++i ) { |
| const SchemaEntry& schema = schemas[i]; |
| converters[schema.name] = schema.func; |
| } |
| return *this; |
| } |
| |
| private: |
| |
| ConverterMap converters; |
| }; |
| } |
| |
| |
| |
| // ------------------------------------------------------------------------------ |
| /** Bundle all the relevant info from a STEP header, parts of which may later |
| * be plainly dumped to the logfile, whereas others may help the caller pick an |
| * appropriate loading strategy.*/ |
| // ------------------------------------------------------------------------------ |
| struct HeaderInfo |
| { |
| std::string timestamp; |
| std::string app; |
| std::string fileSchema; |
| }; |
| |
| |
| // ------------------------------------------------------------------------------ |
| /** Base class for all concrete object instances */ |
| // ------------------------------------------------------------------------------ |
| class Object { |
| public: |
| Object(const char* classname = "unknown") |
| : id( 0 ) |
| , classname(classname) { |
| // empty |
| } |
| |
| virtual ~Object() { |
| // empty |
| } |
| |
| public: |
| |
| // utilities to simplify casting to concrete types |
| template <typename T> |
| const T& To() const { |
| return dynamic_cast<const T&>(*this); |
| } |
| |
| template <typename T> |
| T& To() { |
| return dynamic_cast<T&>(*this); |
| } |
| |
| template <typename T> |
| const T* ToPtr() const { |
| return dynamic_cast<const T*>(this); |
| } |
| |
| template <typename T> |
| T* ToPtr() { |
| return dynamic_cast<T*>(this); |
| } |
| |
| public: |
| uint64_t GetID() const { |
| return id; |
| } |
| |
| std::string GetClassName() const { |
| return classname; |
| } |
| |
| void SetID(uint64_t newval) { |
| id = newval; |
| } |
| |
| private: |
| uint64_t id; |
| const char* const classname; |
| }; |
| |
| template <typename T> |
| size_t GenericFill(const STEP::DB& db, const EXPRESS::LIST& params, T* in); |
| // (intentionally undefined) |
| |
| |
| // ------------------------------------------------------------------------------ |
| /** CRTP shared base class for use by concrete entity implementation classes */ |
| // ------------------------------------------------------------------------------ |
| template <typename TDerived, size_t arg_count> |
| struct ObjectHelper : virtual Object |
| { |
| ObjectHelper() : aux_is_derived(0) {} |
| |
| static Object* Construct(const STEP::DB& db, const EXPRESS::LIST& params) { |
| // make sure we don't leak if Fill() throws an exception |
| std::unique_ptr<TDerived> impl(new TDerived()); |
| |
| // GenericFill<T> is undefined so we need to have a specialization |
| const size_t num_args = GenericFill<TDerived>(db,params,&*impl); |
| (void)num_args; |
| |
| // the following check is commented because it will always trigger if |
| // parts of the entities are generated with dummy wrapper code. |
| // This is currently done to reduce the size of the loader |
| // code. |
| //if (num_args != params.GetSize() && impl->GetClassName() != "NotImplemented") { |
| // DefaultLogger::get()->debug("STEP: not all parameters consumed"); |
| //} |
| return impl.release(); |
| } |
| |
| // note that this member always exists multiple times within the hierarchy |
| // of an individual object, so any access to it must be disambiguated. |
| std::bitset<arg_count> aux_is_derived; |
| }; |
| |
| // ------------------------------------------------------------------------------ |
| /** Class template used to represent OPTIONAL data members in the converted schema */ |
| // ------------------------------------------------------------------------------ |
| template <typename T> |
| struct Maybe |
| { |
| Maybe() : have() {} |
| explicit Maybe(const T& ptr) : ptr(ptr), have(true) { |
| } |
| |
| |
| void flag_invalid() { |
| have = false; |
| } |
| |
| void flag_valid() { |
| have = true; |
| } |
| |
| |
| bool operator! () const { |
| return !have; |
| } |
| |
| operator bool() const { |
| return have; |
| } |
| |
| operator const T&() const { |
| return Get(); |
| } |
| |
| const T& Get() const { |
| ai_assert(have); |
| return ptr; |
| } |
| |
| Maybe& operator=(const T& _ptr) { |
| ptr = _ptr; |
| have = true; |
| return *this; |
| } |
| |
| private: |
| |
| template <typename T2> friend struct InternGenericConvert; |
| |
| operator T&() { |
| return ptr; |
| } |
| |
| T ptr; |
| bool have; |
| }; |
| |
| // ------------------------------------------------------------------------------ |
| /** A LazyObject is created when needed. Before this happens, we just keep |
| the text line that contains the object definition. */ |
| // ------------------------------------------------------------------------------- |
| class LazyObject |
| { |
| friend class DB; |
| public: |
| |
| LazyObject(DB& db, uint64_t id, uint64_t line, const char* type,const char* args); |
| ~LazyObject(); |
| |
| public: |
| |
| Object& operator * () { |
| if (!obj) { |
| LazyInit(); |
| ai_assert(obj); |
| } |
| return *obj; |
| } |
| |
| const Object& operator * () const { |
| if (!obj) { |
| LazyInit(); |
| ai_assert(obj); |
| } |
| return *obj; |
| } |
| |
| template <typename T> |
| const T& To() const { |
| return dynamic_cast<const T&>( **this ); |
| } |
| |
| template <typename T> |
| T& To() { |
| return dynamic_cast<T&>( **this ); |
| } |
| |
| template <typename T> |
| const T* ToPtr() const { |
| return dynamic_cast<const T*>( &**this ); |
| } |
| |
| template <typename T> |
| T* ToPtr() { |
| return dynamic_cast<T*>( &**this ); |
| } |
| |
| Object* operator -> () { |
| return &**this; |
| } |
| |
| const Object* operator -> () const { |
| return &**this; |
| } |
| |
| bool operator== (const std::string& atype) const { |
| return type == atype; |
| } |
| |
| bool operator!= (const std::string& atype) const { |
| return type != atype; |
| } |
| |
| uint64_t GetID() const { |
| return id; |
| } |
| |
| private: |
| |
| void LazyInit() const; |
| |
| private: |
| |
| mutable uint64_t id; |
| const char* const type; |
| DB& db; |
| |
| mutable const char* args; |
| mutable Object* obj; |
| }; |
| |
| template <typename T> |
| inline bool operator==( std::shared_ptr<LazyObject> lo, T whatever ) { |
| return *lo == whatever; // XXX use std::forward if we have 0x |
| } |
| |
| template <typename T> |
| inline bool operator==( const std::pair<uint64_t, std::shared_ptr<LazyObject> >& lo, T whatever ) { |
| return *(lo.second) == whatever; // XXX use std::forward if we have 0x |
| } |
| |
| |
| // ------------------------------------------------------------------------------ |
| /** Class template used to represent lazily evaluated object references in the converted schema */ |
| // ------------------------------------------------------------------------------ |
| template <typename T> |
| struct Lazy |
| { |
| typedef Lazy Out; |
| Lazy(const LazyObject* obj = NULL) : obj(obj) { |
| } |
| |
| operator const T*() const { |
| return obj->ToPtr<T>(); |
| } |
| |
| operator const T&() const { |
| return obj->To<T>(); |
| } |
| |
| const T& operator * () const { |
| return obj->To<T>(); |
| } |
| |
| const T* operator -> () const { |
| return &obj->To<T>(); |
| } |
| |
| const LazyObject* obj; |
| }; |
| |
| // ------------------------------------------------------------------------------ |
| /** Class template used to represent LIST and SET data members in the converted schema */ |
| // ------------------------------------------------------------------------------ |
| template <typename T, uint64_t min_cnt, uint64_t max_cnt=0uL> |
| struct ListOf : public std::vector<typename T::Out> |
| { |
| typedef typename T::Out OutScalar; |
| typedef ListOf Out; |
| |
| |
| ListOf() { |
| static_assert(min_cnt <= max_cnt || !max_cnt, "min_cnt <= max_cnt || !max_cnt"); |
| } |
| |
| }; |
| |
| |
| // ------------------------------------------------------------------------------ |
| template <typename TOut> |
| struct PickBaseType { |
| typedef EXPRESS::PrimitiveDataType<TOut> Type; |
| }; |
| |
| template <typename TOut> |
| struct PickBaseType< Lazy<TOut> > { |
| typedef EXPRESS::ENTITY Type; |
| }; |
| |
| template <> struct PickBaseType< std::shared_ptr< const EXPRESS::DataType > >; |
| |
| // ------------------------------------------------------------------------------ |
| template <typename T> |
| struct InternGenericConvert { |
| void operator()(T& out, const std::shared_ptr< const EXPRESS::DataType >& in, const STEP::DB& /*db*/) { |
| try{ |
| out = dynamic_cast< const typename PickBaseType<T>::Type& > ( *in ); |
| } |
| catch(std::bad_cast&) { |
| throw TypeError("type error reading literal field"); |
| } |
| } |
| }; |
| |
| template <> |
| struct InternGenericConvert< std::shared_ptr< const EXPRESS::DataType > > { |
| void operator()(std::shared_ptr< const EXPRESS::DataType >& out, const std::shared_ptr< const EXPRESS::DataType >& in, const STEP::DB& /*db*/) { |
| out = in; |
| } |
| }; |
| |
| template <typename T> |
| struct InternGenericConvert< Maybe<T> > { |
| void operator()(Maybe<T>& out, const std::shared_ptr< const EXPRESS::DataType >& in, const STEP::DB& db) { |
| GenericConvert((T&)out,in,db); |
| out.flag_valid(); |
| } |
| }; |
| |
| template <typename T,uint64_t min_cnt, uint64_t max_cnt> |
| struct InternGenericConvertList { |
| void operator()(ListOf<T, min_cnt, max_cnt>& out, const std::shared_ptr< const EXPRESS::DataType >& inp_base, const STEP::DB& db) { |
| |
| const EXPRESS::LIST* inp = dynamic_cast<const EXPRESS::LIST*>(inp_base.get()); |
| if (!inp) { |
| throw TypeError("type error reading aggregate"); |
| } |
| |
| // XXX is this really how the EXPRESS notation ([?:3],[1:3]) is intended? |
| if (max_cnt && inp->GetSize() > max_cnt) { |
| DefaultLogger::get()->warn("too many aggregate elements"); |
| } |
| else if (inp->GetSize() < min_cnt) { |
| DefaultLogger::get()->warn("too few aggregate elements"); |
| } |
| |
| out.reserve(inp->GetSize()); |
| for(size_t i = 0; i < inp->GetSize(); ++i) { |
| |
| out.push_back( typename ListOf<T, min_cnt, max_cnt>::OutScalar() ); |
| try{ |
| GenericConvert(out.back(),(*inp)[i], db); |
| } |
| catch(const TypeError& t) { |
| throw TypeError(t.what() +std::string(" of aggregate")); |
| } |
| } |
| } |
| }; |
| |
| template <typename T> |
| struct InternGenericConvert< Lazy<T> > { |
| void operator()(Lazy<T>& out, const std::shared_ptr< const EXPRESS::DataType >& in_base, const STEP::DB& db) { |
| const EXPRESS::ENTITY* in = dynamic_cast<const EXPRESS::ENTITY*>(in_base.get()); |
| if (!in) { |
| throw TypeError("type error reading entity"); |
| } |
| out = Couple<T>(db).GetObject(*in); |
| } |
| }; |
| |
| template <typename T1> |
| inline void GenericConvert(T1& a, const std::shared_ptr< const EXPRESS::DataType >& b, const STEP::DB& db) { |
| return InternGenericConvert<T1>()(a,b,db); |
| } |
| |
| template <typename T1,uint64_t N1, uint64_t N2> |
| inline void GenericConvert(ListOf<T1,N1,N2>& a, const std::shared_ptr< const EXPRESS::DataType >& b, const STEP::DB& db) { |
| return InternGenericConvertList<T1,N1,N2>()(a,b,db); |
| } |
| |
| |
| // ------------------------------------------------------------------------------ |
| /** Lightweight manager class that holds the map of all objects in a |
| * STEP file. DB's are exclusively maintained by the functions in |
| * STEPFileReader.h*/ |
| // ------------------------------------------------------------------------------- |
| class DB |
| { |
| friend DB* ReadFileHeader(std::shared_ptr<IOStream> stream); |
| friend void ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, |
| const char* const* types_to_track, size_t len, |
| const char* const* inverse_indices_to_track, size_t len2 |
| ); |
| |
| friend class LazyObject; |
| |
| public: |
| |
| // objects indexed by ID - this can grow pretty large (i.e some hundred million |
| // entries), so use raw pointers to avoid *any* overhead. |
| typedef std::map<uint64_t,const LazyObject* > ObjectMap; |
| |
| // objects indexed by their declarative type, but only for those that we truly want |
| typedef std::set< const LazyObject*> ObjectSet; |
| typedef std::map<std::string, ObjectSet > ObjectMapByType; |
| |
| // list of types for which to keep inverse indices for all references |
| // that the respective objects keep. |
| // the list keeps pointers to strings in static storage |
| typedef std::set<const char*> InverseWhitelist; |
| |
| // references - for each object id the ids of all objects which reference it |
| // this is used to simulate STEP inverse indices for selected types. |
| typedef std::step_unordered_multimap<uint64_t, uint64_t > RefMap; |
| typedef std::pair<RefMap::const_iterator,RefMap::const_iterator> RefMapRange; |
| |
| private: |
| |
| DB(std::shared_ptr<StreamReaderLE> reader) |
| : reader(reader) |
| , splitter(*reader,true,true) |
| , evaluated_count() |
| , schema( NULL ) |
| {} |
| |
| public: |
| |
| ~DB() { |
| for(ObjectMap::value_type& o : objects) { |
| delete o.second; |
| } |
| } |
| |
| public: |
| |
| uint64_t GetObjectCount() const { |
| return objects.size(); |
| } |
| |
| uint64_t GetEvaluatedObjectCount() const { |
| return evaluated_count; |
| } |
| |
| const HeaderInfo& GetHeader() const { |
| return header; |
| } |
| |
| const EXPRESS::ConversionSchema& GetSchema() const { |
| return *schema; |
| } |
| |
| const ObjectMap& GetObjects() const { |
| return objects; |
| } |
| |
| const ObjectMapByType& GetObjectsByType() const { |
| return objects_bytype; |
| } |
| |
| const RefMap& GetRefs() const { |
| return refs; |
| } |
| |
| |
| bool KeepInverseIndicesForType(const char* const type) const { |
| return inv_whitelist.find(type) != inv_whitelist.end(); |
| } |
| |
| |
| // get the yet unevaluated object record with a given id |
| const LazyObject* GetObject(uint64_t id) const { |
| const ObjectMap::const_iterator it = objects.find(id); |
| if (it != objects.end()) { |
| return (*it).second; |
| } |
| return NULL; |
| } |
| |
| |
| // get an arbitrary object out of the soup with the only restriction being its type. |
| const LazyObject* GetObject(const std::string& type) const { |
| const ObjectMapByType::const_iterator it = objects_bytype.find(type); |
| if (it != objects_bytype.end() && (*it).second.size()) { |
| return *(*it).second.begin(); |
| } |
| return NULL; |
| } |
| |
| // same, but raise an exception if the object doesn't exist and return a reference |
| const LazyObject& MustGetObject(uint64_t id) const { |
| const LazyObject* o = GetObject(id); |
| if (!o) { |
| throw TypeError("requested entity is not present",id); |
| } |
| return *o; |
| } |
| |
| const LazyObject& MustGetObject(const std::string& type) const { |
| const LazyObject* o = GetObject(type); |
| if (!o) { |
| throw TypeError("requested entity of type "+type+"is not present"); |
| } |
| return *o; |
| } |
| |
| |
| #ifdef ASSIMP_IFC_TEST |
| |
| // evaluate *all* entities in the file. this is a power test for the loader |
| void EvaluateAll() { |
| for(ObjectMap::value_type& e :objects) { |
| **e.second; |
| } |
| ai_assert(evaluated_count == objects.size()); |
| } |
| |
| #endif |
| |
| private: |
| |
| // full access only offered to close friends - they should |
| // use the provided getters rather than messing around with |
| // the members directly. |
| LineSplitter& GetSplitter() { |
| return splitter; |
| } |
| |
| void InternInsert(const LazyObject* lz) { |
| objects[lz->GetID()] = lz; |
| |
| const ObjectMapByType::iterator it = objects_bytype.find( lz->type ); |
| if (it != objects_bytype.end()) { |
| (*it).second.insert(lz); |
| } |
| } |
| |
| void SetSchema(const EXPRESS::ConversionSchema& _schema) { |
| schema = &_schema; |
| } |
| |
| |
| void SetTypesToTrack(const char* const* types, size_t N) { |
| for(size_t i = 0; i < N;++i) { |
| objects_bytype[types[i]] = ObjectSet(); |
| } |
| } |
| |
| void SetInverseIndicesToTrack( const char* const* types, size_t N ) { |
| for(size_t i = 0; i < N;++i) { |
| const char* const sz = schema->GetStaticStringForToken(types[i]); |
| ai_assert(sz); |
| inv_whitelist.insert(sz); |
| } |
| } |
| |
| HeaderInfo& GetHeader() { |
| return header; |
| } |
| |
| void MarkRef(uint64_t who, uint64_t by_whom) { |
| refs.insert(std::make_pair(who,by_whom)); |
| } |
| |
| private: |
| HeaderInfo header; |
| ObjectMap objects; |
| ObjectMapByType objects_bytype; |
| RefMap refs; |
| InverseWhitelist inv_whitelist; |
| std::shared_ptr<StreamReaderLE> reader; |
| LineSplitter splitter; |
| uint64_t evaluated_count; |
| const EXPRESS::ConversionSchema* schema; |
| }; |
| |
| } |
| |
| } // end Assimp |
| |
| #endif // INCLUDED_AI_STEPFILE_H |