1 /*=========================================================================
2 
3   Program:   ParaView
4   Module:    vtkSelection.cxx
5 
6   Copyright (c) Kitware, Inc.
7   All rights reserved.
8   See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkSelection.h"
16 
17 #include "vtkAbstractArray.h"
18 #include "vtkAtomicTypes.h"
19 #include "vtkFieldData.h"
20 #include "vtkInformation.h"
21 #include "vtkInformationIntegerKey.h"
22 #include "vtkInformationIterator.h"
23 #include "vtkInformationObjectBaseKey.h"
24 #include "vtkInformationStringKey.h"
25 #include "vtkInformationVector.h"
26 #include "vtkNew.h"
27 #include "vtkObjectFactory.h"
28 #include "vtkSMPTools.h"
29 #include "vtkSelectionNode.h"
30 #include "vtkSignedCharArray.h"
31 #include "vtkTable.h"
32 
33 #include <vtksys/RegularExpression.hxx>
34 #include <vtksys/SystemTools.hxx>
35 
36 #include <cassert>
37 #include <cctype>
38 #include <iterator>
39 #include <map>
40 #include <memory>
41 #include <sstream>
42 #include <string>
43 #include <vector>
44 
45 namespace
46 {
47   // since certain compilers don't support std::to_string yet
48   template <typename T>
convert_to_string(const T & val)49   std::string convert_to_string(const T& val)
50   {
51     std::ostringstream str;
52     str << val;
53     return str.str();
54   }
55 }
56 
57 //============================================================================
58 namespace parser
59 {
60 class Node
61 {
62 public:
63   Node() = default;
64   virtual ~Node() = default;
65   virtual bool Evaluate(vtkIdType offset) const = 0;
66   virtual void Print(ostream& os) const = 0;
67 };
68 
69 class NodeVariable : public Node
70 {
71   vtkSignedCharArray* Data;
72   std::string Name;
73 
74 public:
NodeVariable(vtkSignedCharArray * data,const std::string & name)75   NodeVariable(vtkSignedCharArray* data, const std::string& name)
76     : Data(data)
77     , Name(name)
78   {
79   }
Evaluate(vtkIdType offset) const80   bool Evaluate(vtkIdType offset) const override
81   {
82     assert(this->Data == nullptr || this->Data->GetNumberOfValues() > offset);
83     return this->Data ? (this->Data->GetValue(offset) != 0) : false;
84   }
Print(ostream & os) const85   void Print(ostream& os) const override { os << this->Name; }
86 };
87 
88 class NodeNot : public Node
89 {
90   std::shared_ptr<Node> Child;
91 
92 public:
NodeNot(const std::shared_ptr<Node> & node)93   NodeNot(const std::shared_ptr<Node>& node)
94     : Child(node)
95   {
96   }
Evaluate(vtkIdType offset) const97   bool Evaluate(vtkIdType offset) const override
98   {
99     assert(this->Child);
100     return !this->Child->Evaluate(offset);
101   }
Print(ostream & os) const102   void Print(ostream& os) const override
103   {
104     os << "!";
105     this->Child->Print(os);
106   }
107 };
108 
109 class NodeAnd : public Node
110 {
111   std::shared_ptr<Node> ChildA;
112   std::shared_ptr<Node> ChildB;
113 
114 public:
NodeAnd(const std::shared_ptr<Node> & nodeA,const std::shared_ptr<Node> & nodeB)115   NodeAnd(const std::shared_ptr<Node>& nodeA, const std::shared_ptr<Node>& nodeB)
116     : ChildA(nodeA)
117     , ChildB(nodeB)
118   {
119   }
Evaluate(vtkIdType offset) const120   bool Evaluate(vtkIdType offset) const override
121   {
122     assert(this->ChildA && this->ChildB);
123     return this->ChildA->Evaluate(offset) && this->ChildB->Evaluate(offset);
124   }
Print(ostream & os) const125   void Print(ostream& os) const override
126   {
127     os << "(";
128     this->ChildA->Print(os);
129     os << " & ";
130     this->ChildB->Print(os);
131     os << ")";
132   }
133 };
134 
135 class NodeOr : public Node
136 {
137   std::shared_ptr<Node> ChildA;
138   std::shared_ptr<Node> ChildB;
139 
140 public:
NodeOr(const std::shared_ptr<Node> & nodeA,const std::shared_ptr<Node> & nodeB)141   NodeOr(const std::shared_ptr<Node>& nodeA, const std::shared_ptr<Node>& nodeB)
142     : ChildA(nodeA)
143     , ChildB(nodeB)
144   {
145   }
Evaluate(vtkIdType offset) const146   bool Evaluate(vtkIdType offset) const override
147   {
148     assert(this->ChildA && this->ChildB);
149     return this->ChildA->Evaluate(offset) || this->ChildB->Evaluate(offset);
150   }
Print(ostream & os) const151   void Print(ostream& os) const override
152   {
153     os << "(";
154     this->ChildA->Print(os);
155     os << " | ";
156     this->ChildB->Print(os);
157     os << ")";
158   }
159 };
160 } // namespace parser
161 
162 //============================================================================
163 class vtkSelection::vtkInternals
164 {
165   // applies the operator on the "top" (aka back) of the op_stack to the
166   // variables on the var_stack and pushes the result on the var_stack.
ApplyBack(std::vector<char> & op_stack,std::vector<std::shared_ptr<parser::Node>> & var_stack) const167   bool ApplyBack(
168     std::vector<char>& op_stack, std::vector<std::shared_ptr<parser::Node> >& var_stack) const
169   {
170     assert(op_stack.size() > 0);
171 
172     if (op_stack.back() == '!')
173     {
174       if (var_stack.size() < 1)
175       {
176         // failed
177         return false;
178       }
179       const auto a = var_stack.back();
180       var_stack.pop_back();
181       var_stack.push_back(std::make_shared<parser::NodeNot>(a));
182       // pop the applied operator.
183       op_stack.pop_back();
184       return true;
185     }
186     else if (op_stack.back() == '|' || op_stack.back() == '&')
187     {
188       if (var_stack.size() < 2)
189       {
190         // failed!
191         return false;
192       }
193 
194       const auto b = var_stack.back();
195       var_stack.pop_back();
196       const auto a = var_stack.back();
197       var_stack.pop_back();
198       if (op_stack.back() == '|')
199       {
200         var_stack.push_back(std::make_shared<parser::NodeOr>(a, b));
201       }
202       else
203       {
204         var_stack.push_back(std::make_shared<parser::NodeAnd>(a, b));
205       }
206       // pop the applied operator.
207       op_stack.pop_back();
208       return true;
209     }
210     return false;
211   }
212 
213   // higher the value, higher the precedence.
precedence(char op) const214   inline int precedence(char op) const
215   {
216     switch (op)
217     {
218       case '|':
219         return -15;
220       case '&':
221         return -14;
222       case '!':
223         return -3;
224       case '(':
225       case ')':
226         return -1;
227       default:
228         return -100;
229     }
230   }
231 
232 public:
233   std::map<std::string, vtkSmartPointer<vtkSelectionNode> > Items;
234   vtksys::RegularExpression RegExID;
235 
vtkInternals()236   vtkInternals()
237     : RegExID("^[a-zA-Z0-9]+$")
238   {
239   }
240 
BuildExpressionTree(const std::string & expression,const std::map<std::string,vtkSignedCharArray * > & values_map)241   std::shared_ptr<parser::Node> BuildExpressionTree(
242     const std::string& expression, const std::map<std::string, vtkSignedCharArray*>& values_map)
243   {
244     // We don't use PEGTL since it does not support all supported compilers viz.
245     // VS2013.
246     std::string accumated_text;
247     accumated_text.reserve(expression.size() + 64);
248 
249     std::vector<std::string> parts;
250     for (auto ch : expression)
251     {
252       switch (ch)
253       {
254         case '(':
255         case ')':
256         case '|':
257         case '&':
258         case '!':
259           if (accumated_text.size())
260           {
261             parts.push_back(accumated_text);
262             accumated_text.clear();
263           }
264           parts.push_back(std::string(1, ch));
265           break;
266 
267         default:
268           if (std::isalnum(ch))
269           {
270             accumated_text.push_back(ch);
271           }
272           break;
273       }
274     }
275     if (accumated_text.size())
276     {
277       parts.push_back(accumated_text);
278     }
279 
280     std::vector<std::shared_ptr<parser::Node> > var_stack;
281     std::vector<char> op_stack;
282     for (const auto& term : parts)
283     {
284       if (term[0] == '(')
285       {
286         op_stack.push_back(term[0]);
287       }
288       else if (term[0] == ')')
289       {
290         // apply operators till we encounter the opening paren.
291         while (
292           op_stack.size() > 0 && op_stack.back() != '(' && this->ApplyBack(op_stack, var_stack))
293         {
294         }
295         if (op_stack.size() == 0)
296         {
297           // missing opening paren???
298           return nullptr;
299         }
300         assert(op_stack.back() == '(');
301         // pop the opening paren.
302         op_stack.pop_back();
303       }
304       else if (term[0] == '&' || term[0] == '|' || term[0] == '!')
305       {
306         while (op_stack.size() > 0 && (precedence(term[0]) < precedence(op_stack.back())) &&
307           this->ApplyBack(op_stack, var_stack))
308         {
309         }
310         // push the boolean operator on stack to eval later.
311         op_stack.push_back(term[0]);
312       }
313       else
314       {
315         auto iter = values_map.find(term);
316         auto dataptr = iter != values_map.end() ? iter->second : nullptr;
317         var_stack.push_back(std::make_shared<parser::NodeVariable>(dataptr, term));
318       }
319     }
320 
321     while (!op_stack.empty() && this->ApplyBack(op_stack, var_stack))
322     {
323     }
324     return (op_stack.empty() && var_stack.size() == 1) ? var_stack.front() : nullptr;
325   }
326 };
327 
328 //----------------------------------------------------------------------------
329 vtkStandardNewMacro(vtkSelection);
330 
331 //----------------------------------------------------------------------------
vtkSelection()332 vtkSelection::vtkSelection()
333   : Expression()
334   , Internals(new vtkSelection::vtkInternals())
335 {
336   this->Information->Set(vtkDataObject::DATA_EXTENT_TYPE(), VTK_PIECES_EXTENT);
337   this->Information->Set(vtkDataObject::DATA_PIECE_NUMBER(), -1);
338   this->Information->Set(vtkDataObject::DATA_NUMBER_OF_PIECES(), 1);
339   this->Information->Set(vtkDataObject::DATA_NUMBER_OF_GHOST_LEVELS(), 0);
340 }
341 
342 //----------------------------------------------------------------------------
~vtkSelection()343 vtkSelection::~vtkSelection()
344 {
345   delete this->Internals;
346 }
347 
348 //----------------------------------------------------------------------------
Initialize()349 void vtkSelection::Initialize()
350 {
351   this->Superclass::Initialize();
352   this->RemoveAllNodes();
353   this->Expression.clear();
354 }
355 
356 //----------------------------------------------------------------------------
GetNumberOfNodes() const357 unsigned int vtkSelection::GetNumberOfNodes() const
358 {
359   return static_cast<unsigned int>(this->Internals->Items.size());
360 }
361 
362 //----------------------------------------------------------------------------
GetNode(unsigned int idx) const363 vtkSelectionNode* vtkSelection::GetNode(unsigned int idx) const
364 {
365   const vtkInternals& internals = (*this->Internals);
366   if (static_cast<unsigned int>(internals.Items.size()) > idx)
367   {
368     auto iter = std::next(internals.Items.begin(), static_cast<int>(idx));
369     assert(iter != internals.Items.end());
370     return iter->second;
371   }
372   return nullptr;
373 }
374 
375 //----------------------------------------------------------------------------
GetNode(const std::string & name) const376 vtkSelectionNode* vtkSelection::GetNode(const std::string& name) const
377 {
378   const vtkInternals& internals = (*this->Internals);
379   auto iter = internals.Items.find(name);
380   if (iter != internals.Items.end())
381   {
382     return iter->second;
383   }
384   return nullptr;
385 }
386 
387 //----------------------------------------------------------------------------
AddNode(vtkSelectionNode * node)388 std::string vtkSelection::AddNode(vtkSelectionNode* node)
389 {
390   if (!node)
391   {
392     return std::string();
393   }
394 
395   const vtkInternals& internals = (*this->Internals);
396 
397   // Make sure that node is not already added
398   for (const auto& pair : internals.Items)
399   {
400     if (pair.second == node)
401     {
402       return pair.first;
403     }
404   }
405 
406   static vtkAtomicUInt64 counter = 0;
407   std::string name = std::string("node") + convert_to_string(++counter);
408   while (internals.Items.find(name) != internals.Items.end())
409   {
410     name = std::string("node") + convert_to_string(++counter);
411   }
412 
413   this->SetNode(name, node);
414   return name;
415 }
416 
417 //----------------------------------------------------------------------------
SetNode(const std::string & name,vtkSelectionNode * node)418 void vtkSelection::SetNode(const std::string& name, vtkSelectionNode* node)
419 {
420   vtkInternals& internals = (*this->Internals);
421   if (!node)
422   {
423     vtkErrorMacro("`node` cannot be null.");
424   }
425   else if (!internals.RegExID.find(name))
426   {
427     vtkErrorMacro("`" << name << "` is not in the expected form.");
428   }
429   else if (internals.Items[name] != node)
430   {
431     internals.Items[name] = node;
432     this->Modified();
433   }
434 }
435 
436 //----------------------------------------------------------------------------
GetNodeNameAtIndex(unsigned int idx) const437 std::string vtkSelection::GetNodeNameAtIndex(unsigned int idx) const
438 {
439   const vtkInternals& internals = (*this->Internals);
440   if (static_cast<unsigned int>(internals.Items.size()) > idx)
441   {
442     auto iter = std::next(internals.Items.begin(), static_cast<int>(idx));
443     assert(iter != internals.Items.end());
444     return iter->first;
445   }
446   return std::string();
447 }
448 
449 //----------------------------------------------------------------------------
RemoveNode(unsigned int idx)450 void vtkSelection::RemoveNode(unsigned int idx)
451 {
452   vtkInternals& internals = (*this->Internals);
453   if (static_cast<unsigned int>(internals.Items.size()) > idx)
454   {
455     auto iter = std::next(internals.Items.begin(), static_cast<int>(idx));
456     assert(iter != internals.Items.end());
457     internals.Items.erase(iter);
458     this->Modified();
459   }
460 }
461 
462 //----------------------------------------------------------------------------
RemoveNode(const std::string & name)463 void vtkSelection::RemoveNode(const std::string& name)
464 {
465   vtkInternals& internals = (*this->Internals);
466   if (internals.Items.erase(name) == 1)
467   {
468     this->Modified();
469   }
470 }
471 
472 //----------------------------------------------------------------------------
RemoveNode(vtkSelectionNode * node)473 void vtkSelection::RemoveNode(vtkSelectionNode* node)
474 {
475   vtkInternals& internals = (*this->Internals);
476   for (auto iter = internals.Items.begin(); iter != internals.Items.end(); ++iter)
477   {
478     if (iter->second == node)
479     {
480       internals.Items.erase(iter);
481       this->Modified();
482       break;
483     }
484   }
485 }
486 
487 //----------------------------------------------------------------------------
RemoveAllNodes()488 void vtkSelection::RemoveAllNodes()
489 {
490   vtkInternals& internals = (*this->Internals);
491   if (internals.Items.size())
492   {
493     internals.Items.clear();
494     this->Modified();
495   }
496 }
497 
498 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)499 void vtkSelection::PrintSelf(ostream& os, vtkIndent indent)
500 {
501   this->Superclass::PrintSelf(os, indent);
502   unsigned int numNodes = this->GetNumberOfNodes();
503   os << indent << "Number of nodes: " << numNodes << endl;
504   os << indent << "Nodes: " << endl;
505   for (unsigned int i = 0; i < numNodes; i++)
506   {
507     os << indent << "Node #" << i << endl;
508     this->GetNode(i)->PrintSelf(os, indent.GetNextIndent());
509   }
510 }
511 
512 //----------------------------------------------------------------------------
ShallowCopy(vtkDataObject * src)513 void vtkSelection::ShallowCopy(vtkDataObject* src)
514 {
515   if (auto* ssrc = vtkSelection::SafeDownCast(src))
516   {
517     this->Expression = ssrc->Expression;
518     this->Internals->Items = ssrc->Internals->Items;
519     this->Superclass::ShallowCopy(src);
520     this->Modified();
521   }
522 }
523 
524 //----------------------------------------------------------------------------
DeepCopy(vtkDataObject * src)525 void vtkSelection::DeepCopy(vtkDataObject* src)
526 {
527   if (auto* ssrc = vtkSelection::SafeDownCast(src))
528   {
529     this->Expression = ssrc->Expression;
530 
531     const auto& srcMap = ssrc->Internals->Items;
532     auto& destMap = this->Internals->Items;
533     destMap = srcMap;
534     for (auto& apair : destMap)
535     {
536       vtkNew<vtkSelectionNode> clone;
537       clone->DeepCopy(apair.second);
538       apair.second = clone;
539     }
540     this->Superclass::DeepCopy(src);
541     this->Modified();
542   }
543 }
544 
545 //----------------------------------------------------------------------------
Union(vtkSelection * s)546 void vtkSelection::Union(vtkSelection* s)
547 {
548   for (unsigned int n = 0; n < s->GetNumberOfNodes(); ++n)
549   {
550     this->Union(s->GetNode(n));
551   }
552 }
553 
554 //----------------------------------------------------------------------------
Union(vtkSelectionNode * node)555 void vtkSelection::Union(vtkSelectionNode* node)
556 {
557   bool merged = false;
558   for (unsigned int tn = 0; tn < this->GetNumberOfNodes(); ++tn)
559   {
560     vtkSelectionNode* tnode = this->GetNode(tn);
561     if (tnode->EqualProperties(node))
562     {
563       tnode->UnionSelectionList(node);
564       merged = true;
565       break;
566     }
567   }
568   if (!merged)
569   {
570     vtkSmartPointer<vtkSelectionNode> clone =
571       vtkSmartPointer<vtkSelectionNode>::New();
572     clone->DeepCopy(node);
573     this->AddNode(clone);
574   }
575 }
576 
577 //----------------------------------------------------------------------------
Subtract(vtkSelection * s)578 void vtkSelection::Subtract(vtkSelection* s)
579 {
580   for(unsigned int n=0; n<s->GetNumberOfNodes(); ++n)
581   {
582     this->Subtract(s->GetNode(n));
583   }
584 }
585 
586 //----------------------------------------------------------------------------
Subtract(vtkSelectionNode * node)587 void vtkSelection::Subtract(vtkSelectionNode* node)
588 {
589   bool subtracted = false;
590   for( unsigned int tn = 0; tn<this->GetNumberOfNodes(); ++tn)
591   {
592     vtkSelectionNode* tnode = this->GetNode(tn);
593 
594     if(tnode->EqualProperties(node))
595     {
596       tnode->SubtractSelectionList(node);
597       subtracted = true;
598     }
599   }
600   if( !subtracted )
601   {
602     vtkErrorMacro("Could not subtract selections");
603   }
604 }
605 
606 //----------------------------------------------------------------------------
GetMTime()607 vtkMTimeType vtkSelection::GetMTime()
608 {
609   vtkMTimeType mtime = this->Superclass::GetMTime();
610   const vtkInternals& internals = (*this->Internals);
611   for (const auto& apair : internals.Items)
612   {
613     mtime = std::max(mtime, apair.second->GetMTime());
614   }
615   return mtime;
616 }
617 
618 //----------------------------------------------------------------------------
GetData(vtkInformation * info)619 vtkSelection* vtkSelection::GetData(vtkInformation* info)
620 {
621   return info? vtkSelection::SafeDownCast(info->Get(DATA_OBJECT())) : nullptr;
622 }
623 
624 //----------------------------------------------------------------------------
GetData(vtkInformationVector * v,int i)625 vtkSelection* vtkSelection::GetData(vtkInformationVector* v, int i)
626 {
627   return vtkSelection::GetData(v->GetInformationObject(i));
628 }
629 
630 //----------------------------------------------------------------------------
Evaluate(vtkSignedCharArray * const * values,unsigned int num_values) const631 vtkSmartPointer<vtkSignedCharArray> vtkSelection::Evaluate(
632   vtkSignedCharArray* const* values, unsigned int num_values) const
633 {
634   std::map<std::string, vtkSignedCharArray*> values_map;
635 
636   vtkIdType numVals = -1;
637   unsigned int cc = 0;
638   const vtkInternals& internals = (*this->Internals);
639   for (const auto& apair : internals.Items)
640   {
641     vtkSignedCharArray* array = cc < num_values ? values[cc] : nullptr;
642     if (array == nullptr)
643     {
644       // lets assume null means false.
645     }
646     else
647     {
648       if (array->GetNumberOfComponents() != 1)
649       {
650         vtkGenericWarningMacro("Only single-component arrays are supported!");
651         return nullptr;
652       }
653       if (numVals != -1 && array->GetNumberOfTuples() != numVals)
654       {
655         vtkGenericWarningMacro("Mismatched number of tuples.");
656         return nullptr;
657       }
658       numVals = array->GetNumberOfTuples();
659     }
660     values_map[apair.first] = array;
661     cc++;
662   }
663 
664   std::string expr = this->Expression;
665   if (expr.empty())
666   {
667     bool add_separator = false;
668     std::ostringstream stream;
669     for (const auto& apair : internals.Items)
670     {
671       stream << (add_separator ? "|" : "") << apair.first;
672       add_separator = true;
673     }
674     expr = stream.str();
675   }
676 
677   auto tree = this->Internals->BuildExpressionTree(expr, values_map);
678   if (tree && (values_map.size() > 0))
679   {
680     auto result = vtkSmartPointer<vtkSignedCharArray>::New();
681     result->SetNumberOfComponents(1);
682     result->SetNumberOfTuples(numVals);
683     vtkSMPTools::For(0, numVals, [&](vtkIdType start, vtkIdType end) {
684       for (vtkIdType idx = start; idx < end; ++idx)
685       {
686         result->SetTypedComponent(idx, 0, tree->Evaluate(idx));
687       }
688     });
689     return result;
690   }
691   else if (!tree)
692   {
693     vtkGenericWarningMacro("Failed to parse expression: " << this->Expression);
694   }
695   return nullptr;
696 }
697 
698 //----------------------------------------------------------------------------
Dump()699 void vtkSelection::Dump()
700 {
701   this->Dump(cout);
702 }
703 
704 //----------------------------------------------------------------------------
Dump(ostream & os)705 void vtkSelection::Dump(ostream& os)
706 {
707   vtkSmartPointer<vtkTable> tmpTable = vtkSmartPointer<vtkTable>::New();
708   cerr << "==Selection==" << endl;
709   for (unsigned int i = 0; i < this->GetNumberOfNodes(); ++i)
710   {
711     os << "===Node " << i << "===" << endl;
712     vtkSelectionNode* node = this->GetNode(i);
713     os << "ContentType: ";
714     switch (node->GetContentType())
715     {
716       case vtkSelectionNode::GLOBALIDS:
717         os << "GLOBALIDS";
718         break;
719       case vtkSelectionNode::PEDIGREEIDS:
720         os << "PEDIGREEIDS";
721         break;
722       case vtkSelectionNode::VALUES:
723         os << "VALUES";
724         break;
725       case vtkSelectionNode::INDICES:
726         os << "INDICES";
727         break;
728       case vtkSelectionNode::FRUSTUM:
729         os << "FRUSTUM";
730         break;
731       case vtkSelectionNode::LOCATIONS:
732         os << "LOCATIONS";
733         break;
734       case vtkSelectionNode::THRESHOLDS:
735         os << "THRESHOLDS";
736         break;
737       case vtkSelectionNode::BLOCKS:
738         os << "BLOCKS";
739         break;
740       case vtkSelectionNode::USER:
741         os << "USER";
742         break;
743       default:
744         os << "UNKNOWN";
745         break;
746     }
747     os << endl;
748     os << "FieldType: ";
749     switch (node->GetFieldType())
750     {
751       case vtkSelectionNode::CELL:
752         os << "CELL";
753         break;
754       case vtkSelectionNode::POINT:
755         os << "POINT";
756         break;
757       case vtkSelectionNode::FIELD:
758         os << "FIELD";
759         break;
760       case vtkSelectionNode::VERTEX:
761         os << "VERTEX";
762         break;
763       case vtkSelectionNode::EDGE:
764         os << "EDGE";
765         break;
766       case vtkSelectionNode::ROW:
767         os << "ROW";
768         break;
769       default:
770         os << "UNKNOWN";
771         break;
772     }
773     os << endl;
774     if (node->GetSelectionData())
775     {
776       tmpTable->SetRowData(node->GetSelectionData());
777       tmpTable->Dump(10);
778     }
779   }
780 }
781