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