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_Dump.hxx>
19 #include <Standard_NoMoreObject.hxx>
20 #include <Standard_NullObject.hxx>
21 #include <Standard_Type.hxx>
22 #include <Standard_GUID.hxx>
23 #include <NCollection_Array1.hxx>
24 #include <TCollection_AsciiString.hxx>
25 #include <TDF_Attribute.hxx>
26 #include <TDF_AttributeDelta.hxx>
27 #include <TDF_AttributeIndexedMap.hxx>
28 #include <TDF_AttributeIterator.hxx>
29 #include <TDF_ChildIterator.hxx>
30 #include <TDF_Data.hxx>
31 #include <TDF_Delta.hxx>
32 #include <TDF_DeltaOnAddition.hxx>
33 #include <TDF_DeltaOnForget.hxx>
34 #include <TDF_DeltaOnModification.hxx>
35 #include <TDF_DeltaOnRemoval.hxx>
36 #include <TDF_DeltaOnResume.hxx>
37 #include <TDF_Label.hxx>
38 #include <TDF_LabelNode.hxx>
39 #include <TDF_LabelNodePtr.hxx>
40 #include <TDF_Tool.hxx>
41 #include <TDF_Transaction.hxx>
42 
43 typedef NCollection_Array1<Handle(TDF_AttributeDelta)> TDF_Array1OfAttributeIDelta;
44 
IMPLEMENT_STANDARD_RTTIEXT(TDF_Data,Standard_Transient)45 IMPLEMENT_STANDARD_RTTIEXT(TDF_Data,Standard_Transient)
46 
47 #undef DEB_DELTA_CREATION
48 #define TDF_DATA_COMMIT_OPTIMIZED
49 
50 #ifdef OCCT_DEBUG_DELTA
51 #define TDF_Data_DebugModified(ACTION) \
52   std::cout<<"After "<<ACTION<<" #"<<myTransaction+1<<", DF "<<this<<" had "<<myNbTouchedAtt<<" attribute(s) touched. Time = "<<myTime<<std::endl; \
53 if (!myTransaction) { \
54   TCollection_AsciiString entry; \
55   for (TDF_ChildIterator itr(Root(),Standard_True); itr.More(); itr.Next()) { \
56     const TDF_LabelNode* lnp = itr.Value().myLabelNode; \
57     if (lnp->AttributesModified() || lnp->MayBeModified()) { \
58       TDF_Tool::Entry(itr.Value(),entry); \
59       std::cout<<ACTION<<" on "<<entry<<" : flag(s) "; \
60       if (lnp->AttributesModified()) std::cout<<"AttributesModified "; \
61       if (lnp->MayBeModified()) std::cout<<"MayBeModified already set in transaction 0! Please contact TDF developer."; \
62       std::cout<<std::endl; \
63       std::cout<<itr.Value()<<std::endl; \
64       entry.Clear(); \
65     }}}
66 #else
67 #define TDF_Data_DebugModified(ACTION)
68 #endif
69 
70 #ifdef OCCT_DEBUG_DELTA_CREATION
71 #define TDF_DataDebugDeltaCreation(DELTATYPE) \
72 { \
73 TCollection_AsciiString entry; \
74 TDF_Tool::Entry(currentAtt->Label(),entry); \
75 std::cout<<"Creation of a DeltaOn"<<DELTATYPE<<" \tat "<<entry<<" \ton "<<currentAtt->DynamicType()<<std::endl; \
76 }
77 #else
78 #define TDF_DataDebugDeltaCreation(DELTATYPE)
79 #endif
80 
81 #define TDF_Data_DeltaCreation(DELTACOMMENT,DELTACREATION) \
82 if (withDelta) { \
83   TDF_DataDebugDeltaCreation(DELTACOMMENT); \
84   aDelta->AddAttributeDelta(DELTACREATION); \
85 }
86 
87 //=======================================================================
88 //function : TDF_Data
89 //purpose  : empty constructor
90 //=======================================================================
91 
92 TDF_Data::TDF_Data() :
93 myTransaction           (0),
94 myNbTouchedAtt          (0),
95 myNotUndoMode           (Standard_True),
96 myTime                  (0),
97 myAllowModification     (Standard_True),
98 myAccessByEntries       (Standard_False)
99 {
100   const Handle(NCollection_IncAllocator) anIncAllocator=
101     new NCollection_IncAllocator (16000);
102   myLabelNodeAllocator = anIncAllocator;
103   myRoot = new (anIncAllocator) TDF_LabelNode (this);
104 }
105 
106 //=======================================================================
107 //function : Destroy
108 //purpose  : Used to implement the destructor ~.
109 //=======================================================================
110 
Destroy()111 void TDF_Data::Destroy()
112 {
113   AbortUntilTransaction(1);
114   // Forget the Owner attribute from the root label to avoid referencing document before
115   // desctuction of the framework (on custom attributes forget). Don't call ForgetAll because
116   // it may call backup.
117   while(!myRoot->FirstAttribute().IsNull()) {
118     static Handle(TDF_Attribute) anEmpty;
119     Handle(TDF_Attribute) aFirst = myRoot->FirstAttribute();
120     myRoot->RemoveAttribute(anEmpty, aFirst);
121   }
122   myAccessByEntriesTable.Clear();
123   myRoot->Destroy (myLabelNodeAllocator);
124   myRoot = NULL;
125 }
126 
127 
128 //=======================================================================
129 //function : OpenTransaction
130 //purpose  :
131 //=======================================================================
132 
OpenTransaction()133 Standard_Integer TDF_Data::OpenTransaction()
134 {
135   myTimes.Prepend(myTime);
136   return ++myTransaction;
137 }
138 
139 
140 //=======================================================================
141 //function : CommitTransaction
142 //purpose  : Commits the current transaction.
143 //=======================================================================
144 
Handle(TDF_Delta)145 Handle(TDF_Delta) TDF_Data::CommitTransaction
146 (const Standard_Boolean withDelta)
147 {
148   Handle(TDF_Delta) delta;
149   if (myTransaction>0) {
150     if (withDelta) delta = new TDF_Delta();
151 #ifdef OCCT_DEBUG_DELTA
152     std::cout<<"TDF_Data::Begin Commit #"<<myTransaction<<std::endl;
153 #endif
154 #ifdef TDF_DATA_COMMIT_OPTIMIZED
155     myNbTouchedAtt = 0;
156     if (Root().myLabelNode->MayBeModified())
157 #endif
158       myNbTouchedAtt =
159         TDF_Data::CommitTransaction(Root(),delta,withDelta);
160 
161     if (myNbTouchedAtt && !(withDelta && delta->IsEmpty())) ++myTime;
162     --myTransaction;
163     if (withDelta) {
164       if (!delta->IsEmpty()) {
165         delta->Validity(myTimes.First(),myTime);
166 #ifdef OCCT_DEBUG_DELTA
167         if (myTransaction == 0) {
168           std::cout<<"TDF_Data::Commit generated this delta in t=0:"<<std::endl;
169           delta->Dump(std::cout);
170         }
171 #endif
172       }
173 #ifdef OCCT_DEBUG_DELTA
174       else {
175         if (myTransaction == 0)
176           std::cout<<"TDF_Data::Commit generated NO delta."<<std::endl;
177       }
178 #endif
179     }
180     myTimes.RemoveFirst();
181   }
182   TDF_Data_DebugModified("COMMIT");
183   return delta;
184 }
185 
186 
187 //=======================================================================
188 //function : CommitUntilTransaction
189 //purpose  : Commits the transactions until AND including
190 //           the given transaction index.
191 //=======================================================================
192 
Handle(TDF_Delta)193 Handle(TDF_Delta) TDF_Data::CommitUntilTransaction
194 (const Standard_Integer untilTransaction,
195  const Standard_Boolean withDelta)
196 {
197   Handle(TDF_Delta) delta;
198   if ((untilTransaction>0) && (myTransaction >= untilTransaction)) {
199     while (myTransaction > untilTransaction) {
200       delta = TDF_Data::CommitTransaction(Standard_False);
201     }
202     delta = TDF_Data::CommitTransaction(withDelta);
203   }
204   return delta;
205 }
206 
207 
208 //=======================================================================
209 //function : CommitTransaction
210 //purpose  : Recursive method used to implement the commit action.
211 //=======================================================================
212 
CommitTransaction(const TDF_Label & aLabel,const Handle (TDF_Delta)& aDelta,const Standard_Boolean withDelta)213 Standard_Integer TDF_Data::CommitTransaction
214 (const TDF_Label& aLabel,
215  const Handle(TDF_Delta)& aDelta,
216  const Standard_Boolean withDelta)
217 {
218   aLabel.myLabelNode->MayBeModified(Standard_False);
219   Standard_Integer nbTouchedAtt = 0;
220 #ifdef TDF_DATA_COMMIT_OPTIMIZED
221   Standard_Boolean attMod = aLabel.myLabelNode->AttributesModified();
222 #else
223   Standard_Boolean attMod = Standard_True;
224 #endif
225 
226   if (attMod) {
227     Handle(TDF_Attribute)    lastAtt;
228     Handle(TDF_Attribute)  backupAtt;
229     Standard_Boolean currentIsRemoved = Standard_False;
230     attMod = Standard_False;
231 
232     TDF_AttributeIterator itr1(aLabel, Standard_False);
233     while (itr1.More()) {
234       Handle(TDF_Attribute) aPtrCurrentAtt = itr1.Value();
235       itr1.Next();
236       //      currentAtt = itr1.Value();
237 
238       // A callback:
239       aPtrCurrentAtt->BeforeCommitTransaction();
240 
241       backupAtt  = aPtrCurrentAtt->myBackup;
242 
243       if (aPtrCurrentAtt->myTransaction == myTransaction) {
244         ++nbTouchedAtt;
245         --(aPtrCurrentAtt->myTransaction);
246 
247         // ------------------------------------------------------- Forgotten
248         if (aPtrCurrentAtt->IsForgotten()) {
249           if (aPtrCurrentAtt->mySavedTransaction >=
250               aPtrCurrentAtt->myTransaction)
251           {
252             const Handle(TDF_Attribute) currentAtt = aPtrCurrentAtt;
253             // Collision with a not forgotten version.
254             if (backupAtt.IsNull()) {
255               TDF_Data_DeltaCreation
256                 ("Removal(1)",
257                  currentAtt->DeltaOnRemoval());
258               if (myNotUndoMode) currentAtt->BeforeRemoval();
259               aLabel.myLabelNode->RemoveAttribute(lastAtt,currentAtt);
260               currentIsRemoved = Standard_True;
261               attMod = Standard_True;
262             }
263             else {
264               // Modified then Forgotten...
265               // Forgotten flag spreading?
266               currentAtt->Resume();
267               currentAtt->Restore(backupAtt);
268               currentAtt->myTransaction = backupAtt->myTransaction;
269               currentAtt->RemoveBackup();
270               backupAtt = currentAtt->myBackup;
271               if (myTransaction == 1) {
272                 TDF_Data_DeltaCreation
273                   ("Removal(2)",
274                    currentAtt->DeltaOnRemoval());
275                 if (myNotUndoMode) currentAtt->BeforeRemoval();
276                 aLabel.myLabelNode->RemoveAttribute(lastAtt,currentAtt);
277                 currentIsRemoved = Standard_True;
278               }
279               else {
280                 // BeforeForget has already been called once.
281                 // if (myNotUndoMode) currentAtt->BeforeForget();
282                 currentAtt->Forget(myTransaction-1);
283                 TDF_Data_DeltaCreation
284                   ("Forget(1)",
285                    currentAtt->DeltaOnForget());
286                 attMod = Standard_True;
287               }
288             }
289           }
290           else {
291             // Forgotten in lower transaction than the current one.
292             TDF_Data_DeltaCreation
293               ("Forget(2)",
294                aPtrCurrentAtt->DeltaOnForget());
295           }
296         }
297         // ---------------------------------------------------------- Resumed.
298         else if (aPtrCurrentAtt->mySavedTransaction < 0) {
299           TDF_Data_DeltaCreation
300             ("Resume",
301              aPtrCurrentAtt->DeltaOnResume());
302           aPtrCurrentAtt->mySavedTransaction = 0;
303           attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
304         }
305 
306         // ------------------------------------------------------------ Added.
307         else if (backupAtt.IsNull()) {
308           TDF_Data_DeltaCreation
309             ("Addition",
310              aPtrCurrentAtt->DeltaOnAddition());
311           attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
312         }
313         // --------------------------------------------------------- Modified.
314         else {
315           const TDF_Attribute* anAttrPtr = aPtrCurrentAtt.operator->(); // to avoid ambiguity
316           TDF_Data_DeltaCreation
317             ("Modification",
318              anAttrPtr->DeltaOnModification(backupAtt));
319           if (aPtrCurrentAtt->myTransaction == backupAtt->myTransaction)
320             aPtrCurrentAtt->RemoveBackup();
321           attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
322         }
323 
324       }
325       else attMod = attMod || (aPtrCurrentAtt->myTransaction > 0);
326 
327       if (currentIsRemoved) currentIsRemoved = Standard_False;
328       else lastAtt = aPtrCurrentAtt;
329     }
330     aLabel.myLabelNode->AttributesModified(attMod);
331   }
332 
333   // Iteration on the children to do the same!
334   //------------------------------------------
335   for (TDF_ChildIterator itr2(aLabel); itr2.More(); itr2.Next()) {
336 #ifdef TDF_DATA_COMMIT_OPTIMIZED
337     if (itr2.Value().myLabelNode->MayBeModified())
338 #endif
339       nbTouchedAtt +=
340         TDF_Data::CommitTransaction(itr2.Value(),aDelta,withDelta);
341   }
342 
343   return nbTouchedAtt;
344 }
345 
346 
347 //=======================================================================
348 //function : AbortTransaction
349 //purpose  : Aborts the current transaction.
350 //=======================================================================
351 
AbortTransaction()352 void TDF_Data::AbortTransaction()
353 {
354   if (myTransaction>0)
355     Undo(TDF_Data::CommitTransaction(Standard_True),Standard_False);
356   TDF_Data_DebugModified("New ABORT");
357 }
358 
359 
360 //=======================================================================
361 //function : AbortUntilTransaction
362 //purpose  : Aborts the transactions until AND including the given index.
363 //=======================================================================
364 
AbortUntilTransaction(const Standard_Integer untilTransaction)365 void TDF_Data::AbortUntilTransaction(const Standard_Integer untilTransaction)
366 {
367   if (untilTransaction>0)
368     Undo(TDF_Data::CommitUntilTransaction(untilTransaction,Standard_True),Standard_False);
369 }
370 
371 
372 //=======================================================================
373 //function : IsApplicable
374 //purpose  :
375 //=======================================================================
376 
IsApplicable(const Handle (TDF_Delta)& aDelta) const377 Standard_Boolean TDF_Data::IsApplicable
378 (const Handle(TDF_Delta)& aDelta) const
379 {
380   return !aDelta.IsNull() && aDelta->IsApplicable(myTime);
381 }
382 
383 //=======================================================================
384 //function : FixOrder
385 //purpose  :
386 //=======================================================================
FixOrder(const Handle (TDF_Delta)& theDelta)387 void TDF_Data::FixOrder(const Handle(TDF_Delta)& theDelta)
388 {
389   // make all OnRemoval (which will cause addition of the attribute) are in the end
390   // to do not put two attributes with the same GUID at one label during undo/redo
391   TDF_AttributeDeltaList anOrderedList;
392 
393   const TDF_AttributeDeltaList& attList = theDelta->AttributeDeltas();
394   TDF_ListIteratorOfAttributeDeltaList anIt(attList);
395   for (; anIt.More(); anIt.Next()) { // append not-removal
396     Handle(TDF_AttributeDelta) attDelta = anIt.Value();
397     if (!attDelta->IsKind(STANDARD_TYPE(TDF_DeltaOnRemoval))) {
398       anOrderedList.Append(attDelta);
399     }
400   }
401   for (anIt.Initialize(attList); anIt.More(); anIt.Next()) { // append removal
402     Handle(TDF_AttributeDelta) attDelta = anIt.Value();
403     if (attDelta->IsKind(STANDARD_TYPE(TDF_DeltaOnRemoval))) {
404       anOrderedList.Append(attDelta);
405     }
406   }
407   theDelta->ReplaceDeltaList(anOrderedList);
408 }
409 //=======================================================================
410 //function : Undo
411 //purpose  : Applies a delta to undo  actions.
412 //=======================================================================
413 
Handle(TDF_Delta)414 Handle(TDF_Delta) TDF_Data::Undo(const Handle(TDF_Delta)& aDelta,
415                                  const Standard_Boolean withDelta)
416 {
417   Handle(TDF_Delta) newDelta;
418   if (!aDelta.IsNull ()) {
419     if (aDelta->IsApplicable(myTime)) {
420       if (withDelta) OpenTransaction();
421 #ifdef OCCT_DEBUG_DELTA
422       std::cout<<"TDF_Data::Undo applies this delta:"<<std::endl;
423       aDelta->Dump(std::cout);
424 #endif
425       aDelta->BeforeOrAfterApply(Standard_True);
426       myNotUndoMode = Standard_False;
427       FixOrder(aDelta);
428       aDelta->Apply ();
429       myNotUndoMode = Standard_True;
430       if (withDelta) {
431         newDelta = CommitTransaction(Standard_True);
432         newDelta->Validity(aDelta->EndTime(),aDelta->BeginTime());
433 #ifdef OCCT_DEBUG_DELTA
434         std::cout<<"TDF_Data::Undo, after validity correction, Delta is now available from time \t#"<<newDelta->BeginTime()<<" to time \t#"<<newDelta->EndTime()<<std::endl;
435 #endif
436       }
437       myTime = aDelta->BeginTime();
438       aDelta->BeforeOrAfterApply(Standard_False);
439     }
440   }
441   return newDelta;
442 }
443 
444 //=======================================================================
445 //function : SetAccessByEntries
446 //purpose  :
447 //=======================================================================
448 
SetAccessByEntries(const Standard_Boolean aSet)449 void TDF_Data::SetAccessByEntries(const Standard_Boolean aSet)
450 {
451   myAccessByEntries = aSet;
452 
453   myAccessByEntriesTable.Clear();
454   if (myAccessByEntries) {
455     // Add root label.
456     TCollection_AsciiString anEntry;
457     TDF_Tool::Entry (myRoot, anEntry);
458     myAccessByEntriesTable.Bind (anEntry, myRoot);
459 
460     // Add all other labels.
461     TDF_ChildIterator itr (myRoot, Standard_True);
462     for (; itr.More(); itr.Next()) {
463       const TDF_Label aLabel = itr.Value();
464       TDF_Tool::Entry (aLabel, anEntry);
465       myAccessByEntriesTable.Bind (anEntry, aLabel);
466     }
467   }
468 }
469 
470 //=======================================================================
471 //function : RegisterLabel
472 //purpose  :
473 //=======================================================================
474 
RegisterLabel(const TDF_Label & aLabel)475 void TDF_Data::RegisterLabel(const TDF_Label& aLabel)
476 {
477   TCollection_AsciiString anEntry;
478   TDF_Tool::Entry (aLabel, anEntry);
479   myAccessByEntriesTable.Bind (anEntry, aLabel);
480 }
481 
482 //=======================================================================
483 //function : Dump
484 //purpose  :
485 //=======================================================================
486 
Dump(Standard_OStream & anOS) const487 Standard_OStream& TDF_Data::Dump(Standard_OStream& anOS) const
488 {
489   anOS<<"Dump of a TDF_Data."<<std::endl;
490   anOS<<"Current transaction: "<<myTransaction;
491   anOS<<"; Current tick: "<<myTime<<";"<<std::endl;
492   return anOS;
493 }
494 
495 //=======================================================================
496 //function : DumpJson
497 //purpose  :
498 //=======================================================================
DumpJson(Standard_OStream & theOStream,Standard_Integer) const499 void TDF_Data::DumpJson (Standard_OStream& theOStream, Standard_Integer /*theDepth*/) const
500 {
501   OCCT_DUMP_TRANSIENT_CLASS_BEGIN (theOStream)
502 
503   TCollection_AsciiString aStrForTDF_Label;
504   TDF_Tool::Entry (myRoot, aStrForTDF_Label);
505   OCCT_DUMP_FIELD_VALUE_STRING (theOStream, aStrForTDF_Label)
506 
507   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myTransaction)
508   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myNbTouchedAtt)
509   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myNotUndoMode)
510   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myTime)
511   for (TColStd_ListOfInteger::Iterator aTimeIt (myTimes); aTimeIt.More(); aTimeIt.Next())
512   {
513     const Standard_Integer aTime = aTimeIt.Value();
514     OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, aTime)
515   }
516   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myAllowModification)
517 }
518