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