1 #ifndef CSM_WOLRD_RECORD_H
2 #define CSM_WOLRD_RECORD_H
3 
4 #include <stdexcept>
5 
6 namespace CSMWorld
7 {
8     struct RecordBase
9     {
10         enum State
11         {
12             State_BaseOnly = 0, // defined in base only
13             State_Modified = 1, // exists in base, but has been modified
14             State_ModifiedOnly = 2, // newly created in modified
15             State_Deleted = 3, // exists in base, but has been deleted
16             State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted)
17         };
18 
19         State mState;
20 
21         virtual ~RecordBase();
22 
23         virtual RecordBase *clone() const = 0;
24 
25         virtual RecordBase *modifiedCopy() const = 0;
26 
27         virtual void assign (const RecordBase& record) = 0;
28         ///< Will throw an exception if the types don't match.
29 
30         bool isDeleted() const;
31 
32         bool isErased() const;
33 
34         bool isModified() const;
35     };
36 
37     template <typename ESXRecordT>
38     struct Record : public RecordBase
39     {
40         ESXRecordT mBase;
41         ESXRecordT mModified;
42 
43         Record();
44 
45         Record(State state,
46                 const ESXRecordT *base = 0, const ESXRecordT *modified = 0);
47 
48         RecordBase *clone() const override;
49 
50         RecordBase *modifiedCopy() const override;
51 
52         void assign (const RecordBase& record) override;
53 
54         const ESXRecordT& get() const;
55         ///< Throws an exception, if the record is deleted.
56 
57         ESXRecordT& get();
58         ///< Throws an exception, if the record is deleted.
59 
60         const ESXRecordT& getBase() const;
61         ///< Throws an exception, if the record is deleted. Returns modified, if there is no base.
62 
63         void setModified (const ESXRecordT& modified);
64         ///< Throws an exception, if the record is deleted.
65 
66         void merge();
67         ///< Merge modified into base.
68     };
69 
70     template <typename ESXRecordT>
Record()71     Record<ESXRecordT>::Record()
72     : mBase(), mModified()
73     { }
74 
75     template <typename ESXRecordT>
Record(State state,const ESXRecordT * base,const ESXRecordT * modified)76     Record<ESXRecordT>::Record(State state, const ESXRecordT *base, const ESXRecordT *modified)
77     {
78         if(base)
79             mBase = *base;
80 
81         if(modified)
82             mModified = *modified;
83 
84         this->mState = state;
85     }
86 
87     template <typename ESXRecordT>
modifiedCopy() const88     RecordBase *Record<ESXRecordT>::modifiedCopy() const
89     {
90         return new Record<ESXRecordT> (State_ModifiedOnly, nullptr, &(this->get()));
91     }
92 
93     template <typename ESXRecordT>
clone() const94     RecordBase *Record<ESXRecordT>::clone() const
95     {
96         return new Record<ESXRecordT> (*this);
97     }
98 
99     template <typename ESXRecordT>
assign(const RecordBase & record)100     void Record<ESXRecordT>::assign (const RecordBase& record)
101     {
102         *this = dynamic_cast<const Record<ESXRecordT>& > (record);
103     }
104 
105     template <typename ESXRecordT>
get() const106     const ESXRecordT& Record<ESXRecordT>::get() const
107     {
108         if (mState==State_Erased)
109             throw std::logic_error ("attempt to access a deleted record");
110 
111         return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified;
112     }
113 
114     template <typename ESXRecordT>
get()115     ESXRecordT& Record<ESXRecordT>::get()
116     {
117         if (mState==State_Erased)
118             throw std::logic_error ("attempt to access a deleted record");
119 
120         return mState==State_BaseOnly || mState==State_Deleted ? mBase : mModified;
121     }
122 
123     template <typename ESXRecordT>
getBase() const124     const ESXRecordT& Record<ESXRecordT>::getBase() const
125     {
126         if (mState==State_Erased)
127             throw std::logic_error ("attempt to access a deleted record");
128 
129         return mState==State_ModifiedOnly ? mModified : mBase;
130     }
131 
132     template <typename ESXRecordT>
setModified(const ESXRecordT & modified)133     void Record<ESXRecordT>::setModified (const ESXRecordT& modified)
134     {
135         if (mState==State_Erased)
136             throw std::logic_error ("attempt to modify a deleted record");
137 
138         mModified = modified;
139 
140         if (mState!=State_ModifiedOnly)
141             mState = State_Modified;
142     }
143 
144     template <typename ESXRecordT>
merge()145     void Record<ESXRecordT>::merge()
146     {
147         if (isModified())
148         {
149             mBase = mModified;
150             mState = State_BaseOnly;
151         }
152         else if (mState==State_Deleted)
153         {
154             mState = State_Erased;
155         }
156     }
157 }
158 
159 #endif
160