]> SALOME platform Git repositories - modules/shaper.git/blob - src/FeaturesPlugin/FeaturesPlugin_Validators.cpp
Salome HOME
2ab3adbb06d75f78451ff4e0abf2e655636eb5b0
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_Validators.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        FeaturesPlugin_Validators.cpp
4 // Created:     22 March 2016
5 // Author:      Dmitry Bobylev
6
7 #include "FeaturesPlugin_Validators.h"
8
9 #include <ModelAPI_Attribute.h>
10 #include <ModelAPI_AttributeInteger.h>
11 #include <ModelAPI_AttributeSelectionList.h>
12 #include <ModelAPI_AttributeString.h>
13 #include <ModelAPI_AttributeReference.h>
14 #include <ModelAPI_Feature.h>
15 #include <ModelAPI_ResultCompSolid.h>
16 #include <ModelAPI_ResultConstruction.h>
17
18 #include <GeomValidators_BodyShapes.h>
19 #include <GeomValidators_FeatureKind.h>
20 #include <GeomValidators_ShapeType.h>
21
22 #include <GeomAPI_DataMapOfShapeShape.h>
23 #include <GeomAPI_PlanarEdges.h>
24 #include <GeomAPI_ShapeExplorer.h>
25 #include <GeomAPI_ShapeIterator.h>
26
27 #include <GeomAlgoAPI_ShapeBuilder.h>
28 #include <GeomAlgoAPI_ShapeTools.h>
29 #include <GeomAlgoAPI_WireBuilder.h>
30
31 //==================================================================================================
32 bool FeaturesPlugin_ValidatorPipePath::isValid(const AttributePtr& theAttribute,
33                                                const std::list<std::string>& theArguments,
34                                                std::string& theError) const
35 {
36   AttributeSelectionPtr aPathAttrSelection = std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
37   if(!aPathAttrSelection.get()) {
38     theError = "Error: This validator can only work with path selector in \"Pipe\" feature.";
39     return false;
40   }
41
42   GeomShapePtr aPathShape = aPathAttrSelection->value();
43   ResultPtr aContext = aPathAttrSelection->context();
44   if(!aContext.get()) {
45     theError = "Error: Empty context.";
46     return false;
47   }
48   GeomShapePtr aContextShape = aContext->shape();
49   if(aPathShape.get() && aPathShape->shapeType() == GeomAPI_Shape::WIRE && !aPathShape->isEqual(aContextShape)) {
50     theError = "Error: Local selection of wires not allowed.";
51     return false;
52   }
53
54   return true;
55 }
56
57 //==================================================================================================
58 bool FeaturesPlugin_ValidatorPipeLocations::isValid(const std::shared_ptr<ModelAPI_Feature>& theFeature,
59                                                     const std::list<std::string>& theArguments,
60                                                     std::string& theError) const
61 {
62   static const std::string aCreationMethodID = "creation_method";
63   static const std::string aBaseObjectsID = "base_objects";
64   static const std::string aLocationsID = "locations_objects";
65
66   if(theFeature->getKind() != "Pipe") {
67     theError = "Error: Feature \"" + theFeature->getKind() + "\" does not supported by this validator.";
68     return false;
69   }
70
71   AttributeStringPtr aCreationMethodAttr = theFeature->string(aCreationMethodID);
72   if(!aCreationMethodAttr.get()) {
73     theError = "Error: Could not get \"" + aCreationMethodID + "\" attribute.";
74     return false;
75   }
76
77   if(aCreationMethodAttr->value() != "locations") {
78     return true;
79   }
80
81   AttributeSelectionListPtr aBaseObjectsSelectionList = theFeature->selectionList(aBaseObjectsID);
82   if(!aBaseObjectsSelectionList.get()) {
83     theError = "Error: Could not get \"" + aBaseObjectsID + "\" attribute.";
84     return false;
85   }
86
87   AttributeSelectionListPtr aLocationsSelectionList = theFeature->selectionList(aLocationsID);
88   if(!aLocationsSelectionList.get()) {
89     theError = "Error: Could not get \"" + aBaseObjectsID + "\" attribute.";
90     return false;
91   }
92
93   if(aLocationsSelectionList->size() > 0 && aLocationsSelectionList->size() != aBaseObjectsSelectionList->size()) {
94     theError = "Error: Number of locations should be the same as base objects.";
95     return false;
96   }
97
98   return true;
99 }
100
101 //==================================================================================================
102 bool FeaturesPlugin_ValidatorPipeLocations::isNotObligatory(std::string theFeature, std::string theAttribute)
103 {
104   return false;
105 }
106
107 //==================================================================================================
108 bool FeaturesPlugin_ValidatorBaseForGeneration::isValid(const AttributePtr& theAttribute,
109                                                         const std::list<std::string>& theArguments,
110                                                         std::string& theError) const
111 {
112   if(theArguments.empty()) {
113     theError = "Error: Validator parameters is empty.";
114     return false;
115   }
116
117   // Checking attribute.
118   if(!isValidAttribute(theAttribute, theArguments, theError)) {
119     if(theError.empty()) {
120       theError = "Error: Attribute contains unacceptable shape.";
121     }
122     return false;
123   }
124
125   std::set<ResultConstructionPtr> aSelectedSketches;
126   std::set<ResultConstructionPtr> aSelectedSketchesFromObjects;
127   GeomAPI_DataMapOfShapeShape aSelectedWiresFromObjects;
128   std::string anAttributeType = theAttribute->attributeType();
129   if(anAttributeType == ModelAPI_AttributeSelectionList::typeId()) {
130     AttributeSelectionListPtr aListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
131     for(int anIndex = 0; anIndex < aListAttr->size(); ++anIndex) {
132       AttributeSelectionPtr aSelectionAttr = aListAttr->value(anIndex);
133       ResultPtr aContext = aSelectionAttr->context();
134       if(!aContext.get()) {
135         theError = "Error: Empty context.";
136         return false;
137       }
138
139       ResultConstructionPtr aResultConstruction = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
140       if(!aResultConstruction.get()) {
141         // It is not a result construction. If shape is compound check that it contains only faces and edges.
142         GeomShapePtr aShape = aSelectionAttr->value();
143         if(!aShape.get()) {
144           aShape = aContext->shape();
145         }
146
147         if(aShape->shapeType() == GeomAPI_Shape::COMPOUND) {
148           for(GeomAPI_ShapeIterator anIt(aShape); anIt.more(); anIt.next()) {
149             GeomShapePtr aSubShape = anIt.current();
150             if(aSubShape->shapeType() != GeomAPI_Shape::EDGE
151                 && aSubShape->shapeType() != GeomAPI_Shape::FACE) {
152               theError = "Error: Compound should contain only faces and edges.";
153               return false;
154             }
155           }
156         }
157
158         continue;
159       }
160
161       GeomShapePtr aShape = aSelectionAttr->value();
162       GeomShapePtr aContextShape = aResultConstruction->shape();
163       if(!aShape.get()) {
164         // Whole sketch selected.
165         if(aSelectedSketchesFromObjects.find(aResultConstruction) != aSelectedSketchesFromObjects.cend()) {
166           theError = "Error: Object from this sketch is already selected. Sketch is not allowed for selection.";
167           return false;
168         }
169
170         aSelectedSketches.insert(aResultConstruction);
171       } else {
172         // Object from sketch selected.
173         if(aSelectedSketches.find(aResultConstruction) != aSelectedSketches.cend()) {
174           theError = "Error: Whole sketch with this object is already selected. Don't allow to select this object.";
175           return false;
176         }
177
178         for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::WIRE); anExp.more(); anExp.next()) {
179           GeomShapePtr aWire = anExp.current();
180           if(aWire->orientation() != GeomAPI_Shape::FORWARD) {
181             theError = "Error: Wire with wrong orientation selected.";
182             return false;
183           }
184
185           if(aSelectedWiresFromObjects.isBound(aWire)) {
186             theError = "Error: Objects with such wire already selected. Don't allow to select this object.";
187             return false;
188           }
189
190           aSelectedWiresFromObjects.bind(aWire, aWire);
191           aSelectedSketchesFromObjects.insert(aResultConstruction);
192         }
193       }
194     }
195   }
196
197   return true;
198 }
199
200 //==================================================================================================
201 bool FeaturesPlugin_ValidatorBaseForGeneration::isValidAttribute(const AttributePtr& theAttribute,
202                                                                  const std::list<std::string>& theArguments,
203                                                                  std::string& theError) const
204 {
205   if(!theAttribute.get()) {
206     theError = "Error: Empty attribute.";
207     return false;
208   }
209
210   std::string anAttributeType = theAttribute->attributeType();
211   if(anAttributeType == ModelAPI_AttributeSelectionList::typeId()) {
212     AttributeSelectionListPtr aListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
213     for(int anIndex = 0; anIndex < aListAttr->size(); ++anIndex) {
214       // If at least one attribute is invalid, the result is false.
215       if(!isValidAttribute(aListAttr->value(anIndex), theArguments, theError)) {
216         return false;
217       }
218     }
219   } else if(anAttributeType == ModelAPI_AttributeSelection::typeId()) {
220     // Getting context.
221     AttributeSelectionPtr anAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
222     ResultPtr aContext = anAttr->context();
223     if(!aContext.get()) {
224       theError = "Error: Attribute have empty context.";
225       return false;
226     }
227
228     GeomShapePtr aShape = anAttr->value();
229     GeomShapePtr aContextShape = aContext->shape();
230     if(!aShape.get()) {
231       aShape = aContextShape;
232     }
233     if(!aShape.get()) {
234       theError = "Error: Empty shape selected";
235       return false;
236     }
237
238     ResultConstructionPtr aConstruction = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
239     if(aConstruction.get()) {
240       // Construciotn selected. Check that is is not infinite.
241       if(aConstruction->isInfinite()) {
242         theError = "Error: Infinite constructions is not allowed as base.";
243         return false;
244       }
245
246       if(aShape->isEqual(aContextShape)) {
247         // Whole construction selected. Check that it have faces.
248         if(aConstruction->facesNum() > 0) {
249           return true;
250         }
251       } else {
252         // Shape on construction selected. Check that it is a face or wire.
253         if(aShape->shapeType() == GeomAPI_Shape::WIRE || aShape->shapeType() == GeomAPI_Shape::FACE) {
254           return true;
255         }
256       }
257
258       return false;
259     }
260
261     if(!aShape->isEqual(aContextShape)) {
262       // Local selection on body does not allowed.
263       theError = "Error: Selected shape is in the local selection. Only global selection is allowed.";
264       return false;
265     }
266
267     // Check that object is a shape with allowed type.
268     GeomValidators_ShapeType aShapeTypeValidator;
269     if(!aShapeTypeValidator.isValid(anAttr, theArguments, theError)) {
270       theError = "Error: Selected shape has unacceptable type. Acceptable types are: faces or wires on sketch, "
271                  "whole sketch(if it has at least one face), and whole objects with shape types: ";
272       std::list<std::string>::const_iterator anIt = theArguments.cbegin();
273       theError += *anIt;
274       for(++anIt; anIt != theArguments.cend(); ++anIt) {
275         theError += ", " + *anIt;
276       }
277       return false;
278     }
279
280   } else {
281     theError = "Error: Attribute \"" + anAttributeType + "\" does not supported by this validator.";
282     return false;
283   }
284
285   return true;
286 }
287
288 //==================================================================================================
289 bool FeaturesPlugin_ValidatorCompositeLauncher::isValid(const AttributePtr& theAttribute,
290                                                         const std::list<std::string>& theArguments,
291                                                         std::string& theError) const
292 {
293   if (theAttribute->attributeType() != ModelAPI_AttributeReference::typeId()) {
294     theError = "Error: The attribute with the " + theAttribute->attributeType() + " type is not processed";
295     return false;
296   }
297   if (theArguments.size() != 2) {
298     theError = "Error: Wrong parameters in XML definition for " + theAttribute->attributeType() + " type";
299     return false;
300   }
301   // first argument is for the base attribute, second - for skipping feature kind
302   std::list<std::string>::const_iterator anIt = theArguments.begin();
303   std::string aBaseAttributeId = *anIt;
304   FeaturePtr aFeature = ModelAPI_Feature::feature(theAttribute->owner());
305   AttributePtr aBaseAttribute = aFeature->attribute(aBaseAttributeId);
306   if (!aBaseAttribute.get()) {
307     theError = "Wrong parameters in XML definition for " + theAttribute->attributeType() + " type";
308     return false;
309   }
310   if (aBaseAttribute->isInitialized()) // when base list of composite feature is already filled,
311     // this validator is not necessary anymore
312     return true;
313
314   anIt++;
315   std::string aFeatureAttributeKind = *anIt;
316   GeomValidators_FeatureKind* aValidator = new GeomValidators_FeatureKind();
317   // check whether the selection is on the sketch
318   std::list<std::string> anArguments;
319   anArguments.push_back(aFeatureAttributeKind);
320
321   bool aFeatureKind = aValidator->isValid(theAttribute, theArguments, theError);
322   bool aPlanarFace = false;
323   // check if selection has Face selected
324   GeomValidators_ShapeType* aShapeType = new GeomValidators_ShapeType();
325   anArguments.clear();
326   anArguments.push_back("face");
327   aPlanarFace = aShapeType->isValid(theAttribute, anArguments, theError);
328
329   bool aValid = !aFeatureKind && aPlanarFace;
330   return aValid;
331 }
332
333 //==================================================================================================
334 bool FeaturesPlugin_ValidatorCanBeEmpty::isValid(const std::shared_ptr<ModelAPI_Feature>& theFeature,
335                                                  const std::list<std::string>& theArguments,
336                                                  std::string& theError) const
337 {
338   if(theArguments.size() != 2) {
339     theError = "Error: Validator should be used with 2 parameters for extrusion.";
340     return false;
341   }
342
343   std::list<std::string>::const_iterator anArgsIt = theArguments.begin(), aLast = theArguments.end();
344
345   AttributePtr aCheckAttribute = theFeature->attribute(*anArgsIt);
346   ++anArgsIt;
347
348   if(isShapesCanBeEmpty(aCheckAttribute, theError)) {
349     return true;
350   }
351
352   AttributeSelectionPtr aSelAttr = theFeature->selection(*anArgsIt);
353   if(!aSelAttr.get()) {
354     theError = "Error: Could not get selection attribute \"" + *anArgsIt + "\".";
355     return false;
356   }
357
358   GeomShapePtr aShape = aSelAttr->value();
359   if(!aShape.get()) {
360     ResultPtr aContext = aSelAttr->context();
361     if(!aContext.get()) {
362       theError = "Error: Base objects list contains vertex or edge, so attribute \"" + *anArgsIt
363                + "\" can not be used with default value. Select direction for extrusion.";
364       return false;
365     }
366
367     aShape = aContext->shape();
368   }
369
370   if(!aShape.get()) {
371     theError = "Error: Base objects list contains vertex or edge, so attribute \"" + *anArgsIt
372               + "\" can not be used with default value. Select direction for extrusion.";
373     return false;
374   }
375
376   return true;
377 }
378
379 //==================================================================================================
380 bool FeaturesPlugin_ValidatorCanBeEmpty::isNotObligatory(std::string theFeature, std::string theAttribute)
381 {
382   return false;
383 }
384
385 //==================================================================================================
386 bool FeaturesPlugin_ValidatorCanBeEmpty::isShapesCanBeEmpty(const AttributePtr& theAttribute,
387                                                             std::string& theError) const
388 {
389   if(!theAttribute.get()) {
390     return true;
391   }
392
393   std::string anAttributeType = theAttribute->attributeType();
394   if(anAttributeType == ModelAPI_AttributeSelectionList::typeId()) {
395     AttributeSelectionListPtr aListAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
396     for(int anIndex = 0; anIndex < aListAttr->size(); ++anIndex) {
397       // If at least one attribute is invalid, the result is false.
398       if(!isShapesCanBeEmpty(aListAttr->value(anIndex), theError)) {
399         return false;
400       }
401     }
402   } else if(anAttributeType == ModelAPI_AttributeSelection::typeId()) {
403     // Getting context.
404     AttributeSelectionPtr anAttr = std::dynamic_pointer_cast<ModelAPI_AttributeSelection>(theAttribute);
405     ResultPtr aContext = anAttr->context();
406     if(!aContext.get()) {
407       return false;
408     }
409
410     GeomShapePtr aShape = anAttr->value();
411     GeomShapePtr aContextShape = aContext->shape();
412     if(!aShape.get()) {
413       aShape = aContextShape;
414     }
415     if(!aShape.get()) {
416       return false;
417     }
418
419     if(aShape->shapeType() == GeomAPI_Shape::VERTEX ||
420        aShape->shapeType() == GeomAPI_Shape::EDGE ||
421        !aShape->isPlanar()) {
422       return false;
423     }
424   } else {
425     return false;
426   }
427
428   return true;
429 }
430
431 //==================================================================================================
432 bool FeaturesPlugin_ValidatorBooleanSelection::isValid(const AttributePtr& theAttribute,
433                                                        const std::list<std::string>& theArguments,
434                                                        std::string& theError) const
435 {
436   AttributeSelectionListPtr anAttrSelectionList = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
437   if(!anAttrSelectionList.get()) {
438     theError = "Error: This validator can only work with selection list attributes in \"Boolean\" feature.";
439     return false;
440   }
441   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
442   int anOperationType = aFeature->integer("bool_type")->value();
443
444   for(int anIndex = 0; anIndex < anAttrSelectionList->size(); ++anIndex) {
445     AttributeSelectionPtr anAttrSelection = anAttrSelectionList->value(anIndex);
446     if(!anAttrSelection.get()) {
447       theError = "Error: Empty attribute selection.";
448       return false;
449     }
450     ResultPtr aContext = anAttrSelection->context();
451     if(!aContext.get()) {
452       theError = "Error: Empty selection context.";
453       return false;
454     }
455     ResultConstructionPtr aResultConstruction =
456       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
457     if(aResultConstruction.get()) {
458       theError = "Error: Result construction not allowed for selection.";
459       return false;
460     }
461     std::shared_ptr<GeomAPI_Shape> aShape = anAttrSelection->value();
462     if(!aShape.get()) {
463       aShape = aContext->shape();
464     }
465     if(!aShape.get()) {
466       theError = "Error: Empty shape.";
467       return false;
468     }
469     int aShapeType = aShape->shapeType();
470     if(anOperationType == 1) {
471       // Fuse operation. Allow to select edges, faces and solids.
472       if(aShapeType != GeomAPI_Shape::EDGE &&
473          aShapeType != GeomAPI_Shape::FACE &&
474          aShapeType != GeomAPI_Shape::SOLID &&
475          aShapeType != GeomAPI_Shape::COMPSOLID &&
476          aShapeType != GeomAPI_Shape::COMPOUND) {
477         theError = "Error: Selected shape has the wrong type.";
478         return false;
479       }
480     } else {
481       if(aShapeType != GeomAPI_Shape::SOLID &&
482          aShapeType != GeomAPI_Shape::COMPSOLID &&
483          aShapeType != GeomAPI_Shape::COMPOUND) {
484         theError = "Error: Selected shape has the wrong type.";
485         return false;
486       }
487     }
488   }
489
490   return true;
491 }
492
493 //==================================================================================================
494 bool FeaturesPlugin_ValidatorPartitionSelection::isValid(const AttributePtr& theAttribute,
495                                                          const std::list<std::string>& theArguments,
496                                                          std::string& theError) const
497 {
498   AttributeSelectionListPtr anAttrSelectionList = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
499   if(!anAttrSelectionList.get()) {
500     theError = "Error: This validator can only work with selection list in \"Partition\" feature.";
501     return false;
502   }
503
504   for(int anIndex = 0; anIndex < anAttrSelectionList->size(); ++anIndex) {
505     AttributeSelectionPtr aSelectAttr = anAttrSelectionList->value(anIndex);
506
507     //GeomValidators_BodyShapes aBodyValidator;
508     //if(aBodyValidator.isValid(aSelectAttr, theArguments, theError)) {
509     //  continue;
510     //}
511
512     GeomValidators_FeatureKind aFeatureKindValidator;
513     if(aFeatureKindValidator.isValid(aSelectAttr, theArguments, theError)) {
514       continue;
515     }
516
517     ResultPtr aContext = aSelectAttr->context();
518     ResultConstructionPtr aResultConstruction = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
519     if(aResultConstruction.get()) {
520       theError = "Error: Only body shapes and construction planes are allowed for selection.";
521       return false;
522     }
523
524     ResultCompSolidPtr aResultCompsolid = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(aContext);
525     if(aResultCompsolid.get()) {
526       continue;
527     }
528
529     theError = "Error: Only body shapes and construction planes are allowed for selection.";
530     return false;
531   }
532
533   theError = "";
534   return true;
535 }
536
537 //==================================================================================================
538 bool FeaturesPlugin_ValidatorRemoveSubShapesSelection::isValid(const AttributePtr& theAttribute,
539                                                                const std::list<std::string>& theArguments,
540                                                                std::string& theError) const
541 {
542   AttributeSelectionListPtr aSubShapesAttrList = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
543   if(!aSubShapesAttrList.get()) {
544     theError = "Error: This validator can only work with selection list in \"Remove Sub-Shapes\" feature.";
545     return false;
546   }
547
548   static const std::string aBaseShapeID = "base_shape";
549   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
550   AttributeSelectionPtr aShapeAttrSelection = aFeature->selection(aBaseShapeID);
551
552   if(!aShapeAttrSelection.get()) {
553     theError = "Error: Could not get \"" + aBaseShapeID + "\" attribute.";
554     return false;
555   }
556
557   GeomShapePtr aBaseShape = aShapeAttrSelection->value();
558   ResultPtr aContext = aShapeAttrSelection->context();
559   if(!aContext.get()) {
560     theError = "Error: Empty context.";
561     return false;
562   }
563   if(!aBaseShape.get()) {
564     aBaseShape = aContext->shape();
565   }
566   if(!aBaseShape.get()) {
567     theError = "Error: Empty base shape.";
568     return false;
569   }
570
571   for(int anIndex = 0; anIndex < aSubShapesAttrList->size(); ++anIndex) {
572     bool isSameFound = false;
573     AttributeSelectionPtr anAttrSelectionInList = aSubShapesAttrList->value(anIndex);
574     GeomShapePtr aShapeToAdd = anAttrSelectionInList->value();
575     for(GeomAPI_ShapeIterator anIt(aBaseShape); anIt.more(); anIt.next()) {
576       if(anIt.current()->isEqual(aShapeToAdd)) {
577         isSameFound = true;
578         break;
579       }
580     }
581     if(!isSameFound) {
582       theError = "Error: Only sub-shapes of selected shape is allowed for selection.";
583       return false;
584     }
585   }
586
587   return true;
588 }
589
590 //==================================================================================================
591 bool FeaturesPlugin_ValidatorRemoveSubShapesResult::isValid(const std::shared_ptr<ModelAPI_Feature>& theFeature,
592                                                             const std::list<std::string>& theArguments,
593                                                             std::string& theError) const
594 {
595   static const std::string aBaseShapeID = "base_shape";
596   static const std::string aSubShapesID = "subshapes";
597
598   if(theFeature->getKind() != "Remove_SubShapes") {
599     theError = "Error: Feature \"" + theFeature->getKind() + "\" does not supported by this validator.";
600     return false;
601   }
602
603   AttributeSelectionPtr aShapeAttrSelection = theFeature->selection(aBaseShapeID);
604   if(!aShapeAttrSelection.get()) {
605     theError = "Error: Could not get \"" + aBaseShapeID + "\" attribute.";
606     return false;
607   }
608
609   AttributeSelectionListPtr aSubShapesAttrList = theFeature->selectionList(aSubShapesID);
610   if(!aSubShapesAttrList.get()) {
611     theError = "Error: Could not get \"" + aSubShapesID + "\" attribute.";
612     return false;
613   }
614
615   // Copy base shape.
616   GeomShapePtr aBaseShape = aShapeAttrSelection->value();
617   if(!aBaseShape.get()) {
618     return false;
619   }
620   GeomShapePtr aResultShape = aBaseShape->emptyCopied();
621
622   // Copy sub-shapes from list to new shape.
623   for(int anIndex = 0; anIndex < aSubShapesAttrList->size(); ++anIndex) {
624     AttributeSelectionPtr anAttrSelectionInList = aSubShapesAttrList->value(anIndex);
625     GeomShapePtr aShapeToAdd = anAttrSelectionInList->value();
626     GeomAlgoAPI_ShapeBuilder::add(aResultShape, aShapeToAdd);
627   }
628
629   // Check new shape.
630   if(!GeomAlgoAPI_ShapeTools::isShapeValid(aResultShape)) {
631     theError = "Error: Resulting shape is not valid.";
632     return false;
633   }
634
635   return true;
636 }
637
638 //==================================================================================================
639 bool FeaturesPlugin_ValidatorRemoveSubShapesResult::isNotObligatory(std::string theFeature,
640                                                                     std::string theAttribute)
641 {
642   return false;
643 }