KiCad PCB EDA Suite
vrml2_node.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2016 Cirilo Bernardo <[email protected]>
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 
26 #include <set>
27 #include <map>
28 #include <utility>
29 #include <iterator>
30 #include <cctype>
31 #include <iostream>
32 #include <sstream>
33 #include <algorithm>
34 #include <wx/log.h>
35 
36 #include "vrml2_node.h"
37 
38 
39 static std::set< std::string > badNames;
40 
41 typedef std::pair< std::string, WRL2NODES > NODEITEM;
42 typedef std::map< std::string, WRL2NODES > NODEMAP;
44 
45 
47 {
48  m_sgNode = nullptr;
49  m_Parent = nullptr;
51 
52  if( badNames.empty() )
53  {
54  badNames.insert( "DEF" );
55  badNames.insert( "EXTERNPROTO" );
56  badNames.insert( "FALSE" );
57  badNames.insert( "IS" );
58  badNames.insert( "NULL" );
59  badNames.insert( "PROTO" );
60  badNames.insert( "ROUTE" );
61  badNames.insert( "TO" );
62  badNames.insert( "TRUE" );
63  badNames.insert( "USE" );
64  badNames.insert( "eventIn" );
65  badNames.insert( "eventOut" );
66  badNames.insert( "exposedField" );
67  badNames.insert( "field" );
68  }
69 
70  if( nodenames.empty() )
71  {
72  nodenames.insert( NODEITEM( "Anchor", WRL2NODES::WRL2_ANCHOR ) );
73  nodenames.insert( NODEITEM( "Appearance", WRL2NODES::WRL2_APPEARANCE ) );
74  nodenames.insert( NODEITEM( "Audioclip", WRL2NODES::WRL2_AUDIOCLIP ) );
75  nodenames.insert( NODEITEM( "Background", WRL2NODES::WRL2_BACKGROUND ) );
76  nodenames.insert( NODEITEM( "Billboard", WRL2NODES::WRL2_BILLBOARD ) );
77  nodenames.insert( NODEITEM( "Box", WRL2NODES::WRL2_BOX ) );
78  nodenames.insert( NODEITEM( "Collision", WRL2NODES::WRL2_COLLISION ) );
79  nodenames.insert( NODEITEM( "Color", WRL2NODES::WRL2_COLOR ) );
80  nodenames.insert( NODEITEM( "ColorInterpolator", WRL2NODES::WRL2_COLORINTERPOLATOR ) );
81  nodenames.insert( NODEITEM( "Cone", WRL2NODES::WRL2_CONE ) );
82  nodenames.insert( NODEITEM( "Coordinate", WRL2NODES::WRL2_COORDINATE ) );
83  nodenames.insert( NODEITEM( "CoordinateInterpolator",
85  nodenames.insert( NODEITEM( "Cylinder", WRL2NODES::WRL2_CYLINDER ) );
86  nodenames.insert( NODEITEM( "CylinderSensor", WRL2NODES::WRL2_CYLINDERSENSOR ) );
87  nodenames.insert( NODEITEM( "DirectionalLight", WRL2NODES::WRL2_DIRECTIONALLIGHT ) );
88  nodenames.insert( NODEITEM( "ElevationGrid", WRL2NODES::WRL2_ELEVATIONGRID ) );
89  nodenames.insert( NODEITEM( "Extrusion", WRL2NODES::WRL2_EXTRUSION ) );
90  nodenames.insert( NODEITEM( "Fog", WRL2NODES::WRL2_FOG ) );
91  nodenames.insert( NODEITEM( "FontStyle", WRL2NODES::WRL2_FONTSTYLE ) );
92  nodenames.insert( NODEITEM( "Group", WRL2NODES::WRL2_GROUP ) );
93  nodenames.insert( NODEITEM( "ImageTexture", WRL2NODES::WRL2_IMAGETEXTURE ) );
94  nodenames.insert( NODEITEM( "IndexedFaceSet", WRL2NODES::WRL2_INDEXEDFACESET ) );
95  nodenames.insert( NODEITEM( "IndexedLineSet", WRL2NODES::WRL2_INDEXEDLINESET ) );
96  nodenames.insert( NODEITEM( "Inline", WRL2NODES::WRL2_INLINE ) );
97  nodenames.insert( NODEITEM( "LOD", WRL2NODES::WRL2_LOD ) );
98  nodenames.insert( NODEITEM( "Material", WRL2NODES::WRL2_MATERIAL ) );
99  nodenames.insert( NODEITEM( "MovieTexture", WRL2NODES::WRL2_MOVIETEXTURE ) );
100  nodenames.insert( NODEITEM( "NavigationInfo", WRL2NODES::WRL2_NAVIGATIONINFO ) );
101  nodenames.insert( NODEITEM( "Normal", WRL2NODES::WRL2_NORMAL ) );
102  nodenames.insert( NODEITEM( "NormalInterpolator", WRL2NODES::WRL2_NORMALINTERPOLATOR ) );
103  nodenames.insert( NODEITEM( "OrientationInterpolator",
105  nodenames.insert( NODEITEM( "PixelTexture", WRL2NODES::WRL2_PIXELTEXTURE ) );
106  nodenames.insert( NODEITEM( "PlaneSensor", WRL2NODES::WRL2_PLANESENSOR ) );
107  nodenames.insert( NODEITEM( "PointLight", WRL2NODES::WRL2_POINTLIGHT ) );
108  nodenames.insert( NODEITEM( "PointSet", WRL2NODES::WRL2_POINTSET ) );
109  nodenames.insert( NODEITEM( "PositionInterpolator",
111  nodenames.insert( NODEITEM( "ProximitySensor", WRL2NODES::WRL2_PROXIMITYSENSOR ) );
112  nodenames.insert( NODEITEM( "ScalarInterpolator", WRL2NODES::WRL2_SCALARINTERPOLATOR ) );
113  nodenames.insert( NODEITEM( "Script", WRL2NODES::WRL2_SCRIPT ) );
114  nodenames.insert( NODEITEM( "Shape", WRL2NODES::WRL2_SHAPE ) );
115  nodenames.insert( NODEITEM( "Sound", WRL2NODES::WRL2_SOUND ) );
116  nodenames.insert( NODEITEM( "Sphere", WRL2NODES::WRL2_SPHERE ) );
117  nodenames.insert( NODEITEM( "SphereSensor", WRL2NODES::WRL2_SPHERESENSOR ) );
118  nodenames.insert( NODEITEM( "SpotLight", WRL2NODES::WRL2_SPOTLIGHT ) );
119  nodenames.insert( NODEITEM( "Switch", WRL2NODES::WRL2_SWITCH ) );
120  nodenames.insert( NODEITEM( "Text", WRL2NODES::WRL2_TEXT ) );
121  nodenames.insert( NODEITEM( "TextureCoordinate", WRL2NODES::WRL2_TEXTURECOORDINATE ) );
122  nodenames.insert( NODEITEM( "TextureTransform", WRL2NODES::WRL2_TEXTURETRANSFORM ) );
123  nodenames.insert( NODEITEM( "TimeSensor", WRL2NODES::WRL2_TIMESENSOR ) );
124  nodenames.insert( NODEITEM( "TouchSensor", WRL2NODES::WRL2_TOUCHSENSOR ) );
125  nodenames.insert( NODEITEM( "Transform", WRL2NODES::WRL2_TRANSFORM ) );
126  nodenames.insert( NODEITEM( "ViewPoint", WRL2NODES::WRL2_VIEWPOINT ) );
127  nodenames.insert( NODEITEM( "VisibilitySensor", WRL2NODES::WRL2_VISIBILITYSENSOR ) );
128  nodenames.insert( NODEITEM( "WorldInfo", WRL2NODES::WRL2_WORLDINFO ) );
129  }
130 }
131 
132 
134 {
135  if( m_Parent )
136  m_Parent->unlinkChildNode( this );
137 
138  std::list< WRL2NODE* >::iterator sBP = m_BackPointers.begin();
139  std::list< WRL2NODE* >::iterator eBP = m_BackPointers.end();
140 
141  while( sBP != eBP )
142  {
143  (*sBP)->unlinkRefNode( this );
144  ++sBP;
145  }
146 
147  std::list< WRL2NODE* >::iterator sC = m_Refs.begin();
148  std::list< WRL2NODE* >::iterator eC = m_Refs.end();
149 
150  while( sC != eC )
151  {
152  (*sC)->delNodeRef( this );
153  ++sC;
154  }
155 
156  m_Refs.clear();
157  sC = m_Children.begin();
158  eC = m_Children.end();
159 
160  while( sC != eC )
161  {
162  (*sC)->SetParent( nullptr, false );
163  delete *sC;
164  ++sC;
165  }
166 
167  m_Children.clear();
168 }
169 
170 
172 {
173  // the parent node must never be added as a backpointer
174  if( aNode == m_Parent )
175  return;
176 
177  std::list< WRL2NODE* >::iterator np =
178  std::find( m_BackPointers.begin(), m_BackPointers.end(), aNode );
179 
180  if( np != m_BackPointers.end() )
181  return;
182 
183  m_BackPointers.push_back( aNode );
184 }
185 
186 
188 {
189  std::list< WRL2NODE* >::iterator np =
190  std::find( m_BackPointers.begin(), m_BackPointers.end(), aNode );
191 
192  if( np != m_BackPointers.end() )
193  {
194  m_BackPointers.erase( np );
195  return;
196  }
197 
198  wxLogTrace( traceVrmlPlugin, wxT( "%s:%s:%d\n"
199  " * [BUG] delNodeRef() did not find its target." ),
200  __FILE__, __FUNCTION__, __LINE__ );
201 }
202 
203 
205 {
206  return m_Type;
207 }
208 
209 
211 {
212  return m_Parent;
213 }
214 
215 
216 std::string WRL2NODE::GetName( void )
217 {
218  return m_Name;
219 }
220 
221 
222 bool WRL2NODE::SetName( const std::string& aName )
223 {
224  if( aName.empty() )
225  return false;
226 
227  std::set< std::string >::iterator item = badNames.find( aName );
228 
229  if( item != badNames.end() )
230  {
231  wxLogTrace( traceVrmlPlugin,
232  wxT( "%s:%s:%d\n"
233  " * [INFO] invalid node name '%s' (matches restricted word)" ),
234  __FILE__, __FUNCTION__, __LINE__, *item );
235 
236  return false;
237  }
238 
239 
240  if( isdigit( aName[0] ) )
241  {
242  wxLogTrace( traceVrmlPlugin, wxT( "%s:%s:%d\n"
243  " * [INFO] invalid node name '%s' (begins with digit)" ),
244  __FILE__, __FUNCTION__, __LINE__, aName );
245 
246  return false;
247  }
248 
249  // The characters '+' and '-' are not allowed in names as per the VRML2 specification;
250  // however many parsers accept them and many bad generators use them so the rules
251  // have been relaxed here.
252  // #define BAD_CHARS1 "\"\'#+,-.\\[]{}\x00\x01\x02\x03\x04\x05\x06\x09\x0A\x0B\x0C\x0D\x0E\x0F"
253  #define BAD_CHARS1 "\"\'#,.\\[]{}\x00\x01\x02\x03\x04\x05\x06\x09\x0A\x0B\x0C\x0D\x0E\x0F"
254  #define BAD_CHARS2 "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
255 
256  if( std::string::npos != aName.find_first_of( BAD_CHARS1 )
257  || std::string::npos != aName.find_first_of( BAD_CHARS2 ) )
258  {
259  wxLogTrace( traceVrmlPlugin,
260  wxT( "%s:%s:%d\n"
261  " * [INFO] invalid node name '%s' (contains invalid character)" ),
262  __FILE__, __FUNCTION__, __LINE__, aName );
263 
264  return false;
265  }
266 
267  m_Name = aName;
268 
269  return true;
270 }
271 
272 
273 const char* WRL2NODE::GetNodeTypeName( WRL2NODES aNodeType ) const
274 {
275  if( aNodeType < WRL2NODES::WRL2_BASE || aNodeType >= WRL2NODES::WRL2_END )
276  return "*INVALID_TYPE*";
277 
278  if( aNodeType == WRL2NODES::WRL2_BASE )
279  return "*VIRTUAL_BASE*";
280 
281  NODEMAP::iterator it = nodenames.begin();
282  advance( it, (static_cast<int>( aNodeType ) - static_cast<int>( WRL2NODES::WRL2_BEGIN ) ) );
283 
284  return it->first.c_str();
285 }
286 
287 
288 WRL2NODES WRL2NODE::getNodeTypeID( const std::string& aNodeName )
289 {
290  NODEMAP::iterator it = nodenames.find( aNodeName );
291 
292  if( nodenames.end() != it )
293  return it->second;
294 
296 }
297 
298 
299 std::string WRL2NODE::GetError( void )
300 {
301  return m_error;
302 }
303 
304 
305 WRL2NODE* WRL2NODE::FindNode( const std::string& aNodeName, const WRL2NODE *aCaller )
306 {
307  if( aNodeName.empty() )
308  return nullptr;
309 
310  if( !m_Name.compare( aNodeName ) )
311  return this;
312 
313  std::list< WRL2NODE* >::iterator sLA = m_Children.begin();
314  std::list< WRL2NODE* >::iterator eLA = m_Children.end();
315 
316  WRL2NODE* psg = nullptr;
317 
318  while( sLA != eLA )
319  {
320  if( *sLA != aCaller )
321  {
322  psg = (*sLA)->FindNode( aNodeName, this );
323 
324  if( nullptr != psg )
325  return psg;
326 
327  }
328 
329  ++sLA;
330  }
331 
332  if( nullptr != m_Parent && aCaller != m_Parent )
333  return m_Parent->FindNode( aNodeName, this );
334 
335  return nullptr;
336 }
337 
338 
339 bool WRL2NODE::SetParent( WRL2NODE* aParent, bool doUnlink )
340 {
341  if( aParent == m_Parent )
342  return true;
343 
344  if( nullptr != m_Parent && doUnlink )
345  m_Parent->unlinkChildNode( this );
346 
347  m_Parent = aParent;
348 
349  if( nullptr != m_Parent )
350  m_Parent->AddChildNode( this );
351 
352  return true;
353 }
354 
355 
357 {
358  wxCHECK_MSG( aNode, false, wxT( "Invalid node pointer." ) );
359  wxCHECK_MSG( aNode->GetNodeType() != WRL2NODES::WRL2_BASE, false,
360  wxT( "Attempting to add a base node to another node." ) );
361 
362 
363  std::list< WRL2NODE* >::iterator sC = m_Children.begin();
364  std::list< WRL2NODE* >::iterator eC = m_Children.end();
365 
366  while( sC != eC )
367  {
368  if( *sC == aNode )
369  return false;
370 
371  ++sC;
372  }
373 
374  m_Children.push_back( aNode );
375 
376  if( aNode->GetParent() != this )
377  aNode->SetParent( this );
378 
379  return true;
380 }
381 
382 
384 {
385  wxCHECK_MSG( aNode, false, wxT( "Invalid node pointer." ) );
386  wxCHECK_MSG( aNode->GetNodeType() != WRL2NODES::WRL2_BASE, false,
387  wxT( "Attempt to add a base node reference to another base node" ) );
388 
389  std::list< WRL2NODE* >::iterator sR = m_Refs.begin();
390  std::list< WRL2NODE* >::iterator eR = m_Refs.end();
391 
392  while( sR != eR )
393  {
394  if( *sR == aNode )
395  return true;
396 
397  ++sR;
398  }
399 
400  m_Refs.push_back( aNode );
401  aNode->addNodeRef( this );
402 
403  return true;
404 }
405 
406 
408 {
409  std::list< WRL2NODE* >::iterator sL = m_Children.begin();
410  std::list< WRL2NODE* >::iterator eL = m_Children.end();
411 
412  while( sL != eL )
413  {
414  if( *sL == aNode )
415  {
416  m_Children.erase( sL );
417  return;
418  }
419 
420  ++sL;
421  }
422 }
423 
424 
425 void WRL2NODE::unlinkRefNode( const WRL2NODE* aNode )
426 {
427  std::list< WRL2NODE* >::iterator sL = m_Refs.begin();
428  std::list< WRL2NODE* >::iterator eL = m_Refs.end();
429 
430  while( sL != eL )
431  {
432  if( *sL == aNode )
433  {
434  m_Refs.erase( sL );
435  return;
436  }
437 
438  ++sL;
439  }
440 }
#define BAD_CHARS1
void addNodeRef(WRL2NODE *aNode)
Add a pointer to a node which references but does not own this node.
Definition: vrml2_node.cpp:171
#define BAD_CHARS2
virtual bool SetParent(WRL2NODE *aParent, bool doUnlink=true)
Set the parent WRL2NODE of this object.
Definition: vrml2_node.cpp:339
std::pair< std::string, WRL2NODES > NODEITEM
Definition: vrml2_node.cpp:41
std::list< WRL2NODE * > m_Children
Definition: vrml2_node.h:174
std::string m_Name
Definition: vrml2_node.h:171
std::string GetError(void)
Definition: vrml2_node.cpp:299
virtual std::string GetName(void)
Definition: vrml2_node.cpp:216
std::list< WRL2NODE * > m_Refs
Definition: vrml2_node.h:175
WRL2NODES m_Type
Definition: vrml2_node.h:170
virtual bool SetName(const std::string &aName)
Definition: vrml2_node.cpp:222
WRL2NODES getNodeTypeID(const std::string &aNodeName)
Definition: vrml2_node.cpp:288
std::map< std::string, std::vector< SGNODE * > > NODEMAP
Definition: loadmodel.cpp:101
void delNodeRef(WRL2NODE *aNode)
Remove a pointer to a node which references but does not own this node.
Definition: vrml2_node.cpp:187
virtual ~WRL2NODE()
Definition: vrml2_node.cpp:133
WRL2NODE * m_Parent
Definition: vrml2_node.h:169
static NODEMAP nodenames
Definition: vrml2_node.cpp:43
SGNODE * m_sgNode
Definition: vrml2_node.h:178
std::list< WRL2NODE * > m_BackPointers
Definition: vrml2_node.h:173
WRL2NODES GetNodeType(void) const
Definition: vrml2_node.cpp:204
const wxChar *const traceVrmlPlugin
Flag to enable VRML plugin trace output.
Definition: vrml.cpp:63
const char * GetNodeTypeName(WRL2NODES aNodeType) const
Definition: vrml2_node.cpp:273
virtual bool AddChildNode(WRL2NODE *aNode)
Definition: vrml2_node.cpp:356
std::map< std::string, WRL2NODES > NODEMAP
Definition: vrml2_node.cpp:42
virtual void unlinkRefNode(const WRL2NODE *aNode)
Remove pointers to a referenced node.
Definition: vrml2_node.cpp:425
static std::set< std::string > badNames
Definition: vrml2_node.cpp:39
WRL2NODE * GetParent(void) const
Definition: vrml2_node.cpp:210
WRL2NODES
Definition: wrltypes.h:124
virtual void unlinkChildNode(const WRL2NODE *aNode)
Remove references to an owned child.
Definition: vrml2_node.cpp:407
std::string m_error
Definition: vrml2_node.h:176
virtual bool AddRefNode(WRL2NODE *aNode)
Definition: vrml2_node.cpp:383
virtual WRL2NODE * FindNode(const std::string &aNodeName, const WRL2NODE *aCaller)
Search the tree of linked nodes and returns a reference to the first node found with the given name.
Definition: vrml2_node.cpp:305