0029531: Exception on Redo
[occt.git] / src / TDF / TDF_Data.cxx
CommitLineData
b311480e 1// Created by: DAUTRY Philippe
2// Copyright (c) 1997-1999 Matra Datavision
973c2be1 3// Copyright (c) 1999-2014 OPEN CASCADE SAS
b311480e 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
b311480e 6//
d5f74e42 7// This library is free software; you can redistribute it and/or modify it under
8// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 9// by the Free Software Foundation, with special exception defined in the file
10// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11// distribution for complete text of the license and disclaimer of any warranty.
b311480e 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
b311480e 15
7fd59977 16
42cf5bc1 17#include <NCollection_IncAllocator.hxx>
18#include <Standard_NoMoreObject.hxx>
19#include <Standard_NullObject.hxx>
20#include <Standard_Type.hxx>
a7378539 21#include <Standard_GUID.hxx>
22#include <NCollection_Array1.hxx>
7fd59977 23#include <TCollection_AsciiString.hxx>
7fd59977 24#include <TDF_Attribute.hxx>
42cf5bc1 25#include <TDF_AttributeDelta.hxx>
7fd59977 26#include <TDF_AttributeIndexedMap.hxx>
42cf5bc1 27#include <TDF_AttributeIterator.hxx>
7fd59977 28#include <TDF_ChildIterator.hxx>
42cf5bc1 29#include <TDF_Data.hxx>
7fd59977 30#include <TDF_Delta.hxx>
7fd59977 31#include <TDF_DeltaOnAddition.hxx>
32#include <TDF_DeltaOnForget.hxx>
33#include <TDF_DeltaOnModification.hxx>
34#include <TDF_DeltaOnRemoval.hxx>
35#include <TDF_DeltaOnResume.hxx>
36#include <TDF_Label.hxx>
37#include <TDF_LabelNode.hxx>
38#include <TDF_LabelNodePtr.hxx>
39#include <TDF_Tool.hxx>
42cf5bc1 40#include <TDF_Transaction.hxx>
60d4560d 41
a7378539 42typedef NCollection_Array1<Handle(TDF_AttributeDelta)> TDF_Array1OfAttributeIDelta;
43
25e59720 44IMPLEMENT_STANDARD_RTTIEXT(TDF_Data,Standard_Transient)
92efcf78 45
7fd59977 46#undef DEB_DELTA_CREATION
47#undef TDF_DATA_COMMIT_OPTIMIZED
48
0797d9d3 49#ifdef OCCT_DEBUG_DELTA
7fd59977 50#define TDF_Data_DebugModified(ACTION) \
51 cout<<"After "<<ACTION<<" #"<<myTransaction+1<<", DF "<<this<<" had "<<myNbTouchedAtt<<" attribute(s) touched. Time = "<<myTime<<endl; \
52if (!myTransaction) { \
53 TCollection_AsciiString entry; \
54 for (TDF_ChildIterator itr(Root(),Standard_True); itr.More(); itr.Next()) { \
55 const TDF_LabelNode* lnp = itr.Value().myLabelNode; \
56 if (lnp->AttributesModified() || lnp->MayBeModified()) { \
57 TDF_Tool::Entry(itr.Value(),entry); \
58 cout<<ACTION<<" on "<<entry<<" : flag(s) "; \
59 if (lnp->AttributesModified()) cout<<"AttributesModified "; \
60 if (lnp->MayBeModified()) cout<<"MayBeModified already set in transaction 0! Please contact TDF developer."; \
61 cout<<endl; \
62 cout<<itr.Value()<<endl; \
63 entry.Clear(); \
64 }}}
65#else
66#define TDF_Data_DebugModified(ACTION)
67#endif
68
0797d9d3 69#ifdef OCCT_DEBUG_DELTA_CREATION
7fd59977 70#define TDF_DataDebugDeltaCreation(DELTATYPE) \
71{ \
72TCollection_AsciiString entry; \
73TDF_Tool::Entry(currentAtt->Label(),entry); \
74cout<<"Creation of a DeltaOn"<<DELTATYPE<<" \tat "<<entry<<" \ton "<<currentAtt->DynamicType()<<endl; \
75}
76#else
77#define TDF_DataDebugDeltaCreation(DELTATYPE)
78#endif
79
80#define TDF_Data_DeltaCreation(DELTACOMMENT,DELTACREATION) \
81if (withDelta) { \
82 TDF_DataDebugDeltaCreation(DELTACOMMENT); \
83 aDelta->AddAttributeDelta(DELTACREATION); \
84}
85
7fd59977 86//=======================================================================
87//function : TDF_Data
88//purpose : empty constructor
89//=======================================================================
90
91TDF_Data::TDF_Data() :
92myTransaction (0),
93myNbTouchedAtt (0),
94myNotUndoMode (Standard_True),
95myTime (0),
96myAllowModification (Standard_True)
97{
98 const Handle(NCollection_IncAllocator) anIncAllocator=
99 new NCollection_IncAllocator (16000);
100 myLabelNodeAllocator = anIncAllocator;
101 myRoot = new (anIncAllocator) TDF_LabelNode (this);
102}
103
7fd59977 104//=======================================================================
105//function : Destroy
106//purpose : Used to implement the destructor ~.
107//=======================================================================
108
109void TDF_Data::Destroy()
110{
111 AbortUntilTransaction(1);
60d4560d 112 myRoot->Destroy (myLabelNodeAllocator);
113 myRoot = NULL;
7fd59977 114}
115
116
117//=======================================================================
118//function : OpenTransaction
119//purpose :
120//=======================================================================
121
122Standard_Integer TDF_Data::OpenTransaction()
123{
6af4fe1c 124 myTimes.Prepend(myTime);
7fd59977 125 return ++myTransaction;
126}
127
128
129//=======================================================================
130//function : CommitTransaction
131//purpose : Commits the current transaction.
132//=======================================================================
133
134Handle(TDF_Delta) TDF_Data::CommitTransaction
135(const Standard_Boolean withDelta)
136{
137 Handle(TDF_Delta) delta;
138 if (myTransaction>0) {
139 if (withDelta) delta = new TDF_Delta();
0797d9d3 140#ifdef OCCT_DEBUG_DELTA
7fd59977 141 cout<<"TDF_Data::Begin Commit #"<<myTransaction<<endl;
142#endif
143#ifdef TDF_DATA_COMMIT_OPTIMIZED
144 myNbTouchedAtt = 0;
145 if (Root().myLabelNode->MayBeModified())
146#endif
147 myNbTouchedAtt =
148 TDF_Data::CommitTransaction(Root(),delta,withDelta);
149
150 if (myNbTouchedAtt && !(withDelta && delta->IsEmpty())) ++myTime;
151 --myTransaction;
152 if (withDelta) {
153 if (!delta->IsEmpty()) {
6af4fe1c 154 delta->Validity(myTimes.First(),myTime);
0797d9d3 155#ifdef OCCT_DEBUG_DELTA
7fd59977 156 if (myTransaction == 0) {
157 cout<<"TDF_Data::Commit generated this delta in t=0:"<<endl;
158 delta->Dump(cout);
159 }
160#endif
161 }
0797d9d3 162#ifdef OCCT_DEBUG_DELTA
7fd59977 163 else {
164 if (myTransaction == 0)
165 cout<<"TDF_Data::Commit generated NO delta."<<endl;
166 }
167#endif
168 }
6af4fe1c 169 myTimes.RemoveFirst();
7fd59977 170 }
171 TDF_Data_DebugModified("COMMIT");
172 return delta;
173}
174
175
176//=======================================================================
177//function : CommitUntilTransaction
178//purpose : Commits the transactions until AND including
179// the given transaction index.
180//=======================================================================
181
182Handle(TDF_Delta) TDF_Data::CommitUntilTransaction
183(const Standard_Integer untilTransaction,
184 const Standard_Boolean withDelta)
185{
186 Handle(TDF_Delta) delta;
187 if ((untilTransaction>0) && (myTransaction >= untilTransaction)) {
188 while (myTransaction > untilTransaction) {
189 delta = TDF_Data::CommitTransaction(Standard_False);
190 }
191 delta = TDF_Data::CommitTransaction(withDelta);
192 }
193 return delta;
194}
195
196
197//=======================================================================
198//function : CommitTransaction
199//purpose : Recursive method used to implement the commit action.
200//=======================================================================
201
202Standard_Integer TDF_Data::CommitTransaction
203(const TDF_Label& aLabel,
204 const Handle(TDF_Delta)& aDelta,
205 const Standard_Boolean withDelta)
206{
207 aLabel.myLabelNode->MayBeModified(Standard_False);
208 Standard_Integer nbTouchedAtt = 0;
209#ifdef TDF_DATA_COMMIT_OPTIMIZED
210 Standard_Boolean attMod = aLabel.myLabelNode->AttributesModified();
211#else
212 Standard_Boolean attMod = Standard_True;
213#endif
214
215 if (attMod) {
216 Handle(TDF_Attribute) lastAtt;
217 Handle(TDF_Attribute) backupAtt;
218 Standard_Boolean currentIsRemoved = Standard_False;
219 attMod = Standard_False;
220
221 TDF_AttributeIterator itr1(aLabel, Standard_False);
222 while (itr1.More()) {
fe4f17f0 223 Handle(TDF_Attribute) aPtrCurrentAtt = itr1.Value();
7fd59977 224 itr1.Next();
225 // currentAtt = itr1.Value();
226
7fd59977 227 // A callback:
228 aPtrCurrentAtt->BeforeCommitTransaction();
7fd59977 229
230 backupAtt = aPtrCurrentAtt->myBackup;
231
232 if (aPtrCurrentAtt->myTransaction == myTransaction) {
233 ++nbTouchedAtt;
234 --(aPtrCurrentAtt->myTransaction);
235
236 // ------------------------------------------------------- Forgotten
237 if (aPtrCurrentAtt->IsForgotten()) {
238 if (aPtrCurrentAtt->mySavedTransaction >=
239 aPtrCurrentAtt->myTransaction)
240 {
241 const Handle(TDF_Attribute) currentAtt = aPtrCurrentAtt;
242 // Collision with a not forgotten version.
243 if (backupAtt.IsNull()) {
244 TDF_Data_DeltaCreation
245 ("Removal(1)",
246 currentAtt->DeltaOnRemoval());
247 if (myNotUndoMode) currentAtt->BeforeRemoval();
248 aLabel.myLabelNode->RemoveAttribute(lastAtt,currentAtt);
249 currentIsRemoved = Standard_True;
250 attMod = Standard_True;
251 }
252 else {
253 // Modified then Forgotten...
254 // Forgotten flag spreading?
255 currentAtt->Resume();
256 currentAtt->Restore(backupAtt);
257 currentAtt->myTransaction = backupAtt->myTransaction;
258 currentAtt->RemoveBackup();
259 backupAtt = currentAtt->myBackup;
260 if (myTransaction == 1) {
261 TDF_Data_DeltaCreation
262 ("Removal(2)",
263 currentAtt->DeltaOnRemoval());
264 if (myNotUndoMode) currentAtt->BeforeRemoval();
265 aLabel.myLabelNode->RemoveAttribute(lastAtt,currentAtt);
266 currentIsRemoved = Standard_True;
267 }
268 else {
269 // BeforeForget has already been called once.
270 // if (myNotUndoMode) currentAtt->BeforeForget();
271 currentAtt->Forget(myTransaction-1);
272 TDF_Data_DeltaCreation
273 ("Forget(1)",
274 currentAtt->DeltaOnForget());
275 attMod = Standard_True;
276 }
277 }
278 }
279 else {
280 // Forgotten in lower transaction than the current one.
281 TDF_Data_DeltaCreation
282 ("Forget(2)",
283 aPtrCurrentAtt->DeltaOnForget());
284 }
285 }
286 // ---------------------------------------------------------- Resumed.
287 else if (aPtrCurrentAtt->mySavedTransaction < 0) {
288 TDF_Data_DeltaCreation
289 ("Resume",
290 aPtrCurrentAtt->DeltaOnResume());
291 aPtrCurrentAtt->mySavedTransaction = 0;
292 attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
293 }
294
295 // ------------------------------------------------------------ Added.
296 else if (backupAtt.IsNull()) {
297 TDF_Data_DeltaCreation
298 ("Addition",
299 aPtrCurrentAtt->DeltaOnAddition());
300 attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
301 }
302 // --------------------------------------------------------- Modified.
303 else {
fe4f17f0 304 const TDF_Attribute* anAttrPtr = aPtrCurrentAtt.operator->(); // to avoid ambiguity
7fd59977 305 TDF_Data_DeltaCreation
306 ("Modification",
543a9964 307 anAttrPtr->DeltaOnModification(backupAtt));
7fd59977 308 if (aPtrCurrentAtt->myTransaction == backupAtt->myTransaction)
309 aPtrCurrentAtt->RemoveBackup();
310 attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
311 }
312
313 }
314 else attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
315
316 if (currentIsRemoved) currentIsRemoved = Standard_False;
317 else lastAtt = aPtrCurrentAtt;
318 }
319 aLabel.myLabelNode->AttributesModified(attMod);
320 }
321
322 // Iteration on the children to do the same!
323 //------------------------------------------
324 for (TDF_ChildIterator itr2(aLabel); itr2.More(); itr2.Next()) {
325#ifdef TDF_DATA_COMMIT_OPTIMIZED
326 if (itr2.Value().myLabelNode->MayBeModified())
327#endif
328 nbTouchedAtt +=
329 TDF_Data::CommitTransaction(itr2.Value(),aDelta,withDelta);
330 }
331
332 return nbTouchedAtt;
333}
334
335
336//=======================================================================
337//function : AbortTransaction
338//purpose : Aborts the current transaction.
339//=======================================================================
340
341void TDF_Data::AbortTransaction()
342{
343 if (myTransaction>0)
344 Undo(TDF_Data::CommitTransaction(Standard_True),Standard_False);
345 TDF_Data_DebugModified("New ABORT");
346}
347
348
349//=======================================================================
350//function : AbortUntilTransaction
351//purpose : Aborts the transactions until AND including the given index.
352//=======================================================================
353
354void TDF_Data::AbortUntilTransaction(const Standard_Integer untilTransaction)
355{
356 if (untilTransaction>0)
357 Undo(TDF_Data::CommitUntilTransaction(untilTransaction,Standard_True),Standard_False);
358}
359
360
361//=======================================================================
362//function : IsApplicable
363//purpose :
364//=======================================================================
365
366Standard_Boolean TDF_Data::IsApplicable
367(const Handle(TDF_Delta)& aDelta) const
368{
369 return !aDelta.IsNull() && aDelta->IsApplicable(myTime);
370}
371
a7378539 372//=======================================================================
373//function : FixOrder
374//purpose :
375//=======================================================================
376void TDF_Data::FixOrder(const Handle(TDF_Delta)& theDelta)
377{
f46c2b39 378 // make all OnRemoval (which will cause addition of the attribute) are in the end
379 // to do not put two attributes with the same GUID at one label during undo/redo
380 TDF_AttributeDeltaList anOrderedList;
381
a7378539 382 const TDF_AttributeDeltaList& attList = theDelta->AttributeDeltas();
f46c2b39 383 TDF_ListIteratorOfAttributeDeltaList anIt(attList);
384 for (; anIt.More(); anIt.Next()) { // append not-removal
385 Handle(TDF_AttributeDelta) attDelta = anIt.Value();
386 if (!attDelta->IsKind(STANDARD_TYPE(TDF_DeltaOnRemoval))) {
387 anOrderedList.Append(attDelta);
388 }
a7378539 389 }
f46c2b39 390 for (anIt.Initialize(attList); anIt.More(); anIt.Next()) { // append removal
391 Handle(TDF_AttributeDelta) attDelta = anIt.Value();
392 if (attDelta->IsKind(STANDARD_TYPE(TDF_DeltaOnRemoval))) {
393 anOrderedList.Append(attDelta);
394 }
a7378539 395 }
f46c2b39 396 theDelta->ReplaceDeltaList(anOrderedList);
a7378539 397}
7fd59977 398//=======================================================================
399//function : Undo
400//purpose : Applies a delta to undo actions.
401//=======================================================================
402
a7378539 403Handle(TDF_Delta) TDF_Data::Undo(const Handle(TDF_Delta)& aDelta,
404 const Standard_Boolean withDelta)
7fd59977 405{
406 Handle(TDF_Delta) newDelta;
407 if (!aDelta.IsNull ()) {
408 if (aDelta->IsApplicable(myTime)) {
409 if (withDelta) OpenTransaction();
0797d9d3 410#ifdef OCCT_DEBUG_DELTA
7fd59977 411 cout<<"TDF_Data::Undo applies this delta:"<<endl;
412 aDelta->Dump(cout);
413#endif
414 aDelta->BeforeOrAfterApply(Standard_True);
415 myNotUndoMode = Standard_False;
a7378539 416 FixOrder(aDelta);
7fd59977 417 aDelta->Apply ();
418 myNotUndoMode = Standard_True;
419 if (withDelta) {
420 newDelta = CommitTransaction(Standard_True);
421 newDelta->Validity(aDelta->EndTime(),aDelta->BeginTime());
0797d9d3 422#ifdef OCCT_DEBUG_DELTA
7fd59977 423 cout<<"TDF_Data::Undo, after validity correction, Delta is now available from time \t#"<<newDelta->BeginTime()<<" to time \t#"<<newDelta->EndTime()<<endl;
424#endif
425 }
426 myTime = aDelta->BeginTime();
427 aDelta->BeforeOrAfterApply(Standard_False);
428 }
429 }
430 return newDelta;
431}
432
433
434
435//=======================================================================
436//function : Dump
437//purpose :
438//=======================================================================
439
440Standard_OStream& TDF_Data::Dump(Standard_OStream& anOS) const
441{
442 anOS<<"Dump of a TDF_Data."<<endl;
443 anOS<<"Current transaction: "<<myTransaction;
444 anOS<<"; Current tick: "<<myTime<<";"<<endl;
445 return anOS;
446}