0029142: Exception on Redo.
[occt.git] / src / TDF / TDF_Data.cxx
1 // Created by: DAUTRY Philippe
2 // Copyright (c) 1997-1999 Matra Datavision
3 // Copyright (c) 1999-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
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
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.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16
17 #include <NCollection_IncAllocator.hxx>
18 #include <Standard_NoMoreObject.hxx>
19 #include <Standard_NullObject.hxx>
20 #include <Standard_Type.hxx>
21 #include <Standard_GUID.hxx>
22 #include <NCollection_Array1.hxx>
23 #include <TCollection_AsciiString.hxx>
24 #include <TDF_Attribute.hxx>
25 #include <TDF_AttributeDelta.hxx>
26 #include <TDF_AttributeIndexedMap.hxx>
27 #include <TDF_AttributeIterator.hxx>
28 #include <TDF_ChildIterator.hxx>
29 #include <TDF_Data.hxx>
30 #include <TDF_Delta.hxx>
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>
40 #include <TDF_Transaction.hxx>
41
42 typedef NCollection_Array1<Handle(TDF_AttributeDelta)> TDF_Array1OfAttributeIDelta;
43
44 IMPLEMENT_STANDARD_RTTIEXT(TDF_Data,Standard_Transient)
45
46 #undef DEB_DELTA_CREATION
47 #undef TDF_DATA_COMMIT_OPTIMIZED
48
49 #ifdef OCCT_DEBUG_DELTA
50 #define TDF_Data_DebugModified(ACTION) \
51   cout<<"After "<<ACTION<<" #"<<myTransaction+1<<", DF "<<this<<" had "<<myNbTouchedAtt<<" attribute(s) touched. Time = "<<myTime<<endl; \
52 if (!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
69 #ifdef OCCT_DEBUG_DELTA_CREATION
70 #define TDF_DataDebugDeltaCreation(DELTATYPE) \
71 { \
72 TCollection_AsciiString entry; \
73 TDF_Tool::Entry(currentAtt->Label(),entry); \
74 cout<<"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) \
81 if (withDelta) { \
82   TDF_DataDebugDeltaCreation(DELTACOMMENT); \
83   aDelta->AddAttributeDelta(DELTACREATION); \
84 }
85
86 //=======================================================================
87 //function : TDF_Data
88 //purpose  : empty constructor
89 //=======================================================================
90
91 TDF_Data::TDF_Data() :
92 myTransaction           (0),
93 myNbTouchedAtt          (0),
94 myNotUndoMode           (Standard_True),
95 myTime                  (0),
96 myAllowModification     (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
104 //=======================================================================
105 //function : Destroy
106 //purpose  : Used to implement the destructor ~.
107 //=======================================================================
108
109 void TDF_Data::Destroy()
110 {
111   AbortUntilTransaction(1);
112   myRoot->Destroy (myLabelNodeAllocator);
113   myRoot = NULL;
114 }
115
116
117 //=======================================================================
118 //function : OpenTransaction
119 //purpose  : 
120 //=======================================================================
121
122 Standard_Integer TDF_Data::OpenTransaction() 
123 {
124   myTimes.Prepend(myTime);
125   return ++myTransaction;
126 }
127
128
129 //=======================================================================
130 //function : CommitTransaction
131 //purpose  : Commits the current transaction.
132 //=======================================================================
133
134 Handle(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();
140 #ifdef OCCT_DEBUG_DELTA
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()) {
154         delta->Validity(myTimes.First(),myTime);
155 #ifdef OCCT_DEBUG_DELTA
156         if (myTransaction == 0) {
157           cout<<"TDF_Data::Commit generated this delta in t=0:"<<endl;
158           delta->Dump(cout);
159         }
160 #endif
161       }
162 #ifdef OCCT_DEBUG_DELTA
163       else {
164         if (myTransaction == 0)
165           cout<<"TDF_Data::Commit generated NO delta."<<endl;
166       }
167 #endif
168     }
169     myTimes.RemoveFirst();
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
182 Handle(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
202 Standard_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()) {
223       Handle(TDF_Attribute) aPtrCurrentAtt = itr1.Value();
224       itr1.Next();
225       //      currentAtt = itr1.Value();
226
227       // A callback:
228       aPtrCurrentAtt->BeforeCommitTransaction();
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 {
304           const TDF_Attribute* anAttrPtr = aPtrCurrentAtt.operator->(); // to avoid ambiguity
305           TDF_Data_DeltaCreation
306             ("Modification",
307              anAttrPtr->DeltaOnModification(backupAtt));
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
341 void 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
354 void 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
366 Standard_Boolean TDF_Data::IsApplicable
367 (const Handle(TDF_Delta)& aDelta) const
368 {
369   return !aDelta.IsNull() && aDelta->IsApplicable(myTime);
370 }
371
372 //=======================================================================
373 //function : FixOrder
374 //purpose  : 
375 //=======================================================================
376 void TDF_Data::FixOrder(const Handle(TDF_Delta)& theDelta)
377 {
378   const TDF_AttributeDeltaList& attList = theDelta->AttributeDeltas();
379   Handle(TDF_AttributeDelta) attDelta;
380   Handle(TDF_Attribute) att;
381   Standard_Integer i, indx1(0), indx2(0);
382   Standard_GUID aGuid;
383   TDF_ListIteratorOfAttributeDeltaList itr(attList) ;
384   for (i=1; itr.More(); itr.Next(), i++) {
385     attDelta = itr.Value();
386     if(indx1) {
387       att = attDelta->Attribute();
388       if((att->ID() == aGuid) && (attDelta->IsKind(STANDARD_TYPE(TDF_DeltaOnAddition)))) {
389         indx2 = i;
390         break;
391       }
392     } else 
393       if (attDelta->IsKind(STANDARD_TYPE(TDF_DeltaOnRemoval))) {
394         att = attDelta->Attribute();
395         aGuid = att->ID();
396         indx1 = i;
397       }        
398   }
399   if(indx1 && indx2) {
400     TDF_Array1OfAttributeIDelta anArray(1, attList.Extent());
401     itr.Initialize(attList);
402     for (i=1; itr.More(); itr.Next(), i++) 
403       anArray.SetValue(i, itr.Value());
404     Handle(TDF_AttributeDelta) attDelta1, attDelta2;
405     attDelta1 = anArray.Value(indx1);
406     attDelta2 = anArray.Value(indx2);
407     anArray.SetValue(indx1, attDelta2);
408     anArray.SetValue(indx2, attDelta1);
409     TDF_AttributeDeltaList attList2;
410     for(i=1; i<= anArray.Upper(); i++)
411       attList2.Append(anArray.Value(i));
412     theDelta->ReplaceDeltaList(attList2);
413   }
414 }
415 //=======================================================================
416 //function : Undo
417 //purpose  : Applies a delta to undo  actions.
418 //=======================================================================
419
420 Handle(TDF_Delta) TDF_Data::Undo(const Handle(TDF_Delta)& aDelta,
421                                  const Standard_Boolean withDelta)
422 {
423   Handle(TDF_Delta) newDelta;
424   if (!aDelta.IsNull ()) {
425     if (aDelta->IsApplicable(myTime)) {
426       if (withDelta) OpenTransaction();
427 #ifdef OCCT_DEBUG_DELTA
428       cout<<"TDF_Data::Undo applies this delta:"<<endl;
429       aDelta->Dump(cout);
430 #endif
431       aDelta->BeforeOrAfterApply(Standard_True);
432       myNotUndoMode = Standard_False;
433       FixOrder(aDelta);
434       aDelta->Apply ();
435       myNotUndoMode = Standard_True;
436       if (withDelta) {
437         newDelta = CommitTransaction(Standard_True);
438         newDelta->Validity(aDelta->EndTime(),aDelta->BeginTime());
439 #ifdef OCCT_DEBUG_DELTA
440         cout<<"TDF_Data::Undo, after validity correction, Delta is now available from time \t#"<<newDelta->BeginTime()<<" to time \t#"<<newDelta->EndTime()<<endl;
441 #endif
442       }
443       myTime = aDelta->BeginTime();
444       aDelta->BeforeOrAfterApply(Standard_False);
445     }
446   }
447   return newDelta;
448 }
449
450
451
452 //=======================================================================
453 //function : Dump
454 //purpose  : 
455 //=======================================================================
456
457 Standard_OStream& TDF_Data::Dump(Standard_OStream& anOS) const
458 {
459   anOS<<"Dump of a TDF_Data."<<endl;
460   anOS<<"Current transaction: "<<myTransaction;
461   anOS<<"; Current tick: "<<myTime<<";"<<endl;
462   return anOS;
463 }