Salome HOME
Task #3237: Allow usage of accented characters in ObjectBrowser
[modules/shaper.git] / src / Selector / Selector_Modify.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <Selector_Modify.h>
21
22 #include <Selector_NameGenerator.h>
23 #include <Selector_NExplode.h>
24
25 #include <Locale_Convert.h>
26
27 #include <TNaming_NamedShape.hxx>
28 #include <TNaming_Iterator.hxx>
29 #include <TNaming_SameShapeIterator.hxx>
30 #include <TNaming_NewShapeIterator.hxx>
31 #include <TNaming_Tool.hxx>
32 #include <TDataStd_Name.hxx>
33 #include <TDataStd_Integer.hxx>
34 #include <TDF_ChildIterator.hxx>
35 #include <TopTools_MapOfShape.hxx>
36 #include <TopExp_Explorer.hxx>
37 #include <BRep_Tool.hxx>
38 #include <TopoDS.hxx>
39 #include <TopoDS_Builder.hxx>
40 #include <TopoDS_Compound.hxx>
41
42 Selector_Modify::Selector_Modify() : Selector_Algo()
43 {
44   myWeakIndex = -1; // no index by default
45 }
46
47 // adds to theResult all labels that contain initial shapes for theValue located in theFinal
48 static void findBases(TDF_Label theAccess, Handle(TNaming_NamedShape) theFinal,
49   const TopoDS_Shape& theValue,
50   bool aMustBeAtFinal, const TDF_Label& theAdditionalDocument, TDF_LabelList& theResult)
51 {
52   bool aFoundAnyShape = false;
53   if (TNaming_Tool::HasLabel(theAccess, theValue)) {
54     TNaming_SameShapeIterator aLabIter(theValue, theAccess);
55     for(; aLabIter.More(); aLabIter.Next()) {
56       Handle(TNaming_NamedShape) aNS;
57       if (aLabIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
58         if (aMustBeAtFinal && aNS != theFinal)
59           continue; // looking for old at the same final label only
60         TNaming_Evolution anEvolution = aNS->Evolution();
61         if (anEvolution == TNaming_PRIMITIVE) {
62           // check that this is not in the results already
63           const TDF_Label aResult = aNS->Label();
64           TDF_LabelList::Iterator aResIter(theResult);
65           for(; aResIter.More(); aResIter.Next()) {
66             if (aResIter.Value().IsEqual(aResult))
67               break;
68           }
69           if (!aResIter.More()) // not found, so add this new
70             theResult.Append(aResult);
71           aFoundAnyShape = true;
72         }
73         if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
74           for(TNaming_Iterator aThisIter(aNS); aThisIter.More(); aThisIter.Next()) {
75             if (aThisIter.NewShape().IsSame(theValue)) {
76               // continue recursively, null NS means that any NS are ok
77               findBases(theAccess, theFinal, aThisIter.OldShape(),
78                 false, theAdditionalDocument, theResult);
79               aFoundAnyShape = true;
80             }
81           }
82         }
83       }
84     }
85   }
86   if (!aFoundAnyShape && !theAdditionalDocument.IsNull()) { // try to find in additional document
87     static TDF_Label anEmpty;
88     if (TNaming_Tool::HasLabel(theAdditionalDocument, theValue))
89       findBases(theAdditionalDocument, Handle(TNaming_NamedShape)(), theValue,
90         false, anEmpty, theResult);
91   }
92 }
93
94 /// Returns in theResults all shapes with history started in theBase and ended in theFinal
95 static void findFinals(const TDF_Label& anAccess, const TopoDS_Shape& theBase,
96   const TDF_Label& theFinal,
97   const TDF_Label& theAdditionalDoc, TopTools_MapOfShape& thePass, TopTools_MapOfShape& theResults)
98 {
99   if (TNaming_Tool::HasLabel(anAccess, theBase)) {
100     for(TNaming_NewShapeIterator aBaseIter(theBase, anAccess); aBaseIter.More(); aBaseIter.Next())
101     {
102       TNaming_Evolution anEvolution = aBaseIter.NamedShape()->Evolution();
103       if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
104         if (aBaseIter.NamedShape()->Label().IsEqual(theFinal)) {
105           theResults.Add(aBaseIter.Shape());
106         } else {
107           if (thePass.Add(aBaseIter.Shape()))
108             findFinals(
109               anAccess, aBaseIter.Shape(), theFinal, theAdditionalDoc, thePass, theResults);
110         }
111       }
112     }
113   }
114   if (!theAdditionalDoc.IsNull()) { // search additionally by the additional access label
115     static TDF_Label anEmpty;
116     TopTools_MapOfShape aPass;
117     findFinals(theAdditionalDoc, theBase, theFinal, anEmpty, aPass, theResults);
118   }
119 }
120
121 void Selector_Modify::findModificationResult(TopoDS_ListOfShape& theCommon) {
122   for(TDF_LabelList::Iterator aBase(myBases); aBase.More(); aBase.Next()) {
123     TDF_Label anAdditionalDoc; // this document if base is started in extra document
124     if (aBase.Value().Root() != label().Root()) {
125       anAdditionalDoc = label();
126     }
127     TopTools_MapOfShape aFinals;
128     TopTools_MapOfShape aPass;
129     for(TNaming_Iterator aBaseShape(aBase.Value()); aBaseShape.More(); aBaseShape.Next()) {
130       findFinals(aBase.Value(), aBaseShape.NewShape(), myFinal, anAdditionalDoc, aPass, aFinals);
131     }
132     if (!aFinals.IsEmpty()) {
133       if (theCommon.IsEmpty()) { // just copy all to common
134         for(TopTools_MapOfShape::Iterator aFinal(aFinals); aFinal.More(); aFinal.Next()) {
135           theCommon.Append(aFinal.Key());
136         }
137       } else { // keep only shapes presented in both lists
138         for(TopoDS_ListOfShape::Iterator aCommon(theCommon); aCommon.More(); ) {
139           if (aFinals.Contains(aCommon.Value())) {
140             aCommon.Next();
141           } else { // common is not found, remove it
142             theCommon.Remove(aCommon);
143           }
144         }
145       }
146     }
147   }
148 }
149
150 bool Selector_Modify::select(NCollection_List<Handle(TNaming_NamedShape)>& theModifList,
151   const TopoDS_Shape theContext, const TopoDS_Shape theValue)
152 {
153   if (theModifList.Extent() > 1) { // searching for the best modification result: by context
154     Handle(TNaming_NamedShape) aCandidate;
155     NCollection_List<Handle(TNaming_NamedShape)>::Iterator aModIter(theModifList);
156     for (; !theModifList.IsEmpty() && aModIter.More(); aModIter.Next()) {
157       aCandidate = aModIter.Value();
158       TDF_Label aFatherLab = aCandidate->Label().Father();
159       Handle(TNaming_NamedShape) aFatherNS;
160       if (aFatherLab.FindAttribute(TNaming_NamedShape::GetID(), aFatherNS)) {
161         for (TNaming_Iterator anIter(aFatherNS); anIter.More(); anIter.Next()) {
162           if (theContext.IsSame(anIter.NewShape())) { // found the best modification
163             theModifList.Clear();
164             break;
165           }
166         }
167       }
168     }
169     // take the best candidate, or the last in the iteration
170     theModifList.Clear();
171     theModifList.Append(aCandidate);
172   }
173
174   if (!theModifList.IsEmpty()) {
175     // searching for all the base shapes of this modification
176     findBases(label(), theModifList.First(), theValue, true, baseDocument(), myBases);
177     if (!myBases.IsEmpty()) {
178       myFinal = theModifList.First()->Label();
179       TopoDS_ListOfShape aCommon;
180       findModificationResult(aCommon);
181       // trying to search by neighbors
182       if (aCommon.Extent() > 1) { // more complicated selection
183         if (alwaysGeometricalNaming()) {
184           TopoDS_ListOfShape::Iterator aCommonIter(aCommon);
185           TopoDS_Shape aFirst = aCommonIter.Value();
186           for (aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
187             if (!sameGeometry(aFirst, aCommonIter.Value()))
188               break;
189           }
190           if (!aCommonIter.More()) { // all geometry is same, result is a compound
191             return true;
192           }
193         }
194       } else if (aCommon.Extent() == 1) {
195         return true; // simple modification
196       }
197       if (useNeighbors()) { // optimization: for the current moment only in one case this method is
198                             //  called where this is not needed if neighbors option is disabled
199         // weak naming between the common results
200         Selector_NExplode aNexp(aCommon);
201         myWeakIndex = aNexp.index(theValue);
202       } else
203         myWeakIndex = 0;
204       return myWeakIndex != -1;
205     }
206     // weak naming case
207     TopoDS_ListOfShape aCommon;
208     myFinal = theModifList.First()->Label();
209     Handle(TNaming_NamedShape) aNS;
210     if (myFinal.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
211       for(TNaming_Iterator aFinalIter(aNS); aFinalIter.More(); aFinalIter.Next()) {
212         const TopoDS_Shape& aNewShape = aFinalIter.NewShape();
213         if (!aNewShape.IsNull())
214           aCommon.Append(aNewShape);
215       }
216     }
217     Selector_NExplode aNexp(aCommon);
218     myWeakIndex = aNexp.index(theValue);
219     return myWeakIndex != -1;
220   }
221   return false;
222 }
223
224 void Selector_Modify::store()
225 {
226   storeType(Selector_Algo::SELTYPE_MODIFICATION);
227   storeBaseArray(myBases, myFinal);
228   if (myWeakIndex != -1) {
229     TDataStd_Integer::Set(label(), weakID(), myWeakIndex);
230   }
231 }
232
233 bool Selector_Modify::restore()
234 {
235   if (restoreBaseArray(myBases, myFinal)) {
236     Handle(TDataStd_Integer) aWeakInt;
237     if (label().FindAttribute(weakID(), aWeakInt)) {
238       myWeakIndex = aWeakInt->Get();
239     }
240     return true;
241   }
242   return false;
243 }
244
245 TDF_Label Selector_Modify::restoreByName(std::wstring theName,
246   const TopAbs_ShapeEnum theShapeType, Selector_NameGenerator* theNameGenerator)
247 {
248   TDF_Label aContext;
249   for(size_t anEnd, aStart = 0; aStart != std::string::npos; aStart = anEnd) {
250     if (aStart != 0)
251       aStart++;
252     anEnd = theName.find('&', aStart);
253     std::wstring aSubStr =
254       theName.substr(aStart, anEnd == std::string::npos ? anEnd : anEnd - aStart);
255     if (aSubStr.find(weakNameID()) == 0) { // weak name identifier
256       std::wstring aWeakIndex = aSubStr.substr(weakNameID().size());
257       myWeakIndex = std::stoi(aWeakIndex.c_str());
258       continue;
259     }
260     TDF_Label aSubContext, aValue;
261     if (!theNameGenerator->restoreContext(aSubStr, aSubContext, aValue))
262       return TDF_Label(); // can not restore
263     if(aSubContext.IsNull() || aValue.IsNull())
264       return TDF_Label(); // can not restore
265     if (myFinal.IsNull()) {
266       myFinal = aValue;
267       aContext = aSubContext;
268     } else
269       myBases.Append(aValue);
270   }
271   return aContext;
272 }
273
274 bool Selector_Modify::solve(const TopoDS_Shape& theContext)
275 {
276   TopoDS_Shape aResult;
277   if (myBases.IsEmpty() && myWeakIndex > 0) { // weak name by the final shapes index
278     TopoDS_ListOfShape aCommon;
279     Handle(TNaming_NamedShape) aNS;
280     if (myFinal.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
281       for(TNaming_Iterator aFinalIter(aNS); aFinalIter.More(); aFinalIter.Next()) {
282         const TopoDS_Shape& aNewShape = aFinalIter.NewShape();
283         if (!aNewShape.IsNull())
284           aCommon.Append(aNewShape);
285       }
286     }
287     Selector_NExplode aNexp(aCommon);
288     aResult = aNexp.shape(myWeakIndex);
289   } else { // standard case
290     TopoDS_ListOfShape aFinalsCommon; // final shapes presented in all results from bases
291     findModificationResult(aFinalsCommon);
292     if (aFinalsCommon.Extent() == 1) { // result is valid: found only one shape
293       aResult = aFinalsCommon.First();
294       findNewVersion(theContext, aResult);
295     } else if (aFinalsCommon.Extent() > 1 && myWeakIndex > 0) {
296       Selector_NExplode aNExp(aFinalsCommon);
297       aResult = aNExp.shape(myWeakIndex);
298       findNewVersion(theContext, aResult);
299     } else if (aFinalsCommon.Extent() > 1 && geometricalNaming()) {// if same geometry - compound
300       TopoDS_ListOfShape::Iterator aCommonIter(aFinalsCommon);
301       TopoDS_Shape aFirst = aCommonIter.Value();
302       for(aCommonIter.Next(); aCommonIter.More(); aCommonIter.Next()) {
303         if (!sameGeometry(aFirst, aCommonIter.Value()))
304           break;
305       }
306       if (!aCommonIter.More()) { // all geometry is same, create a result compound
307         TopoDS_Builder aBuilder;
308         TopoDS_Compound aCompound;
309         aBuilder.MakeCompound(aCompound);
310         for(aCommonIter.Initialize(aFinalsCommon); aCommonIter.More(); aCommonIter.Next()) {
311           TopoDS_Shape aSub = aCommonIter.Value();
312           findNewVersion(theContext, aSub);
313           aBuilder.Add(aCompound, aSub);
314         }
315         aResult = aCompound;
316       }
317
318     }
319   }
320
321   if (!aResult.IsNull()) {
322     Selector_Algo::store(aResult);
323     return true;
324   }
325   return false;
326 }
327
328 std::wstring Selector_Modify::name(Selector_NameGenerator* theNameGenerator)
329 {
330   // final&base1&base2 +optionally: [weak_name_1]
331   std::wstring aResult;
332   Handle(TDataStd_Name) aName;
333   if (!myFinal.FindAttribute(TDataStd_Name::GetID(), aName))
334     return L"";
335   aResult += theNameGenerator->contextName(myFinal) + L"/";
336   aResult += Locale::Convert::toWString(aName->Get().ToExtString());
337   for(TDF_LabelList::iterator aBase = myBases.begin(); aBase != myBases.end(); aBase++) {
338     if (!aBase->FindAttribute(TDataStd_Name::GetID(), aName))
339       return L"";
340     aResult += L"&";
341     aResult += theNameGenerator->contextName(*aBase) + L"/";
342     aResult += Locale::Convert::toWString(aName->Get().ToExtString());
343   }
344   if (myWeakIndex != -1) {
345     std::wostringstream aWeakStr;
346     aWeakStr<<L"&"<<weakNameID()<<myWeakIndex;
347     aResult += aWeakStr.str();
348   }
349   return aResult;
350 }