1 /*  $Id: node.cpp 147457 2008-12-10 18:21:48Z ivanovp $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Lewis Geer
27  *
28  */
29 
30 #include <ncbi_pch.hpp>
31 #include <corelib/ncbiutil.hpp>
32 #include <corelib/ncbithr.hpp>
33 #include <corelib/ncbi_safe_static.hpp>
34 #include <html/node.hpp>
35 #include <html/html_exception.hpp>
36 
37 
38 BEGIN_NCBI_SCOPE
39 
40 
41 // Store global exception handling flags in TLS
42 static CStaticTls<CNCBINode::TExceptionFlags> s_TlsExceptionFlags;
43 
44 
CNCBINode(void)45 CNCBINode::CNCBINode(void)
46     : m_CreateSubNodesCalled(false),
47       m_RepeatCount(1),
48       m_RepeatTag(false)
49 {
50     return;
51 }
52 
53 
CNCBINode(const string & name)54 CNCBINode::CNCBINode(const string& name)
55     : m_CreateSubNodesCalled(false),
56       m_Name(name),
57       m_RepeatCount(1),
58       m_RepeatTag(false)
59 {
60     return;
61 }
62 
63 
CNCBINode(const char * name)64 CNCBINode::CNCBINode(const char* name)
65     : m_CreateSubNodesCalled(false),
66       m_Name(name),
67       m_RepeatCount(1),
68       m_RepeatTag(false)
69 {
70     return;
71 }
72 
73 
~CNCBINode(void)74 CNCBINode::~CNCBINode(void)
75 {
76     return;
77 }
78 
79 
s_CheckEndlessRecursion(const CNCBINode * parent,const CNCBINode * child)80 static bool s_CheckEndlessRecursion(const CNCBINode* parent,
81                                     const CNCBINode* child)
82 {
83     if ( !parent  ||  !child  ||  !child->HaveChildren() ) {
84         return false;
85     }
86     ITERATE ( CNCBINode::TChildren, i, child->Children() ) {
87         const CNCBINode* cnode = parent->Node(i);
88         if ( parent == cnode ) {
89             return true;
90         }
91         if ( cnode->HaveChildren()  &&
92              s_CheckEndlessRecursion(parent, cnode)) {
93             return true;
94         }
95     }
96     return false;
97 }
98 
99 
DoAppendChild(CNCBINode * child)100 void CNCBINode::DoAppendChild(CNCBINode* child)
101 {
102     // Check endless recursion
103     TExceptionFlags flags = GetExceptionFlags();
104     if ( (flags  &  CNCBINode::fDisableCheckRecursion) == 0 ) {
105         if ( this == child ) {
106             NCBI_THROW(CHTMLException, eEndlessRecursion,
107                 "Endless recursion: current and child nodes are identical");
108         }
109         if ( s_CheckEndlessRecursion(this, child) ) {
110             NCBI_THROW(CHTMLException, eEndlessRecursion,
111                 "Endless recursion: appended node contains current node " \
112                 "in the child nodes list");
113         }
114     }
115     GetChildren().push_back(CRef<ncbi::CNCBINode>(child));
116 }
117 
118 
RemoveChild(CNCBINode * child)119 CNodeRef CNCBINode::RemoveChild(CNCBINode* child)
120 {
121     CNodeRef ref(child);
122 
123     if ( child  &&  HaveChildren() ) {
124         SIZE_TYPE prev_size = Children().size();
125         // Remove all child nodes from the list.
126         TChildren& children = Children();
127         // It is better to use Children().remove_if(...) here,
128         // but WorkShop's version works only with plain functions :(
129         typedef TChildren::iterator TChildrenIt;
130         for (TChildrenIt it = children.begin(); it != children.end(); ) {
131             if ( it->GetPointer() == child ) {
132                 TChildrenIt cur = it;
133                 ++it;
134                 children.erase(cur);
135             } else {
136                 ++it;
137             }
138         }
139 #  if !NCBI_LIGHTWEIGHT_LIST
140         if ( !children.size() ) {
141             m_Children.release();
142         }
143 #  endif
144         if ( children.size() != prev_size ) {
145             return ref;
146         }
147     }
148     NCBI_THROW(CHTMLException, eNotFound,
149                "Specified node is not a child of the current node");
150     // not reached
151     return CNodeRef(0);
152 }
153 
154 
RemoveChild(CNodeRef & child)155 CNodeRef CNCBINode::RemoveChild(CNodeRef& child)
156 {
157     return RemoveChild(child.GetPointer());
158 }
159 
160 
RemoveAllChildren(void)161 void CNCBINode::RemoveAllChildren(void)
162 {
163 #if NCBI_LIGHTWEIGHT_LIST
164     m_Children.clear();
165 #else
166     m_Children.reset(0);
167 #endif
168 }
169 
170 
HaveAttribute(const string & name) const171 bool CNCBINode::HaveAttribute(const string& name) const
172 {
173     if ( HaveAttributes() ) {
174         TAttributes::const_iterator ptr = Attributes().find(name);
175         if ( ptr != Attributes().end() ) {
176             return true;
177         }
178     }
179     return false;
180 }
181 
182 
GetAttribute(const string & name) const183 const string& CNCBINode::GetAttribute(const string& name) const
184 {
185     if ( HaveAttributes() ) {
186         TAttributes::const_iterator ptr = Attributes().find(name);
187         if ( ptr != Attributes().end() ) {
188             return ptr->second;
189         }
190     }
191     return NcbiEmptyString;
192 }
193 
194 
AttributeIsOptional(const string & name) const195 bool CNCBINode::AttributeIsOptional(const string& name) const
196 {
197     if ( HaveAttributes() ) {
198         TAttributes::const_iterator ptr = Attributes().find(name);
199         if ( ptr != Attributes().end() ) {
200             return ptr->second.IsOptional();
201         }
202     }
203     return true;
204 }
205 
206 
AttributeIsOptional(const char * name) const207 bool CNCBINode::AttributeIsOptional(const char* name) const
208 {
209     return AttributeIsOptional(string(name));
210 }
211 
212 
GetAttributeValue(const string & name) const213 const string* CNCBINode::GetAttributeValue(const string& name) const
214 {
215     if ( HaveAttributes() ) {
216         TAttributes::const_iterator ptr = Attributes().find(name);
217         if ( ptr != Attributes().end() ) {
218             return &ptr->second.GetValue();
219         }
220     }
221     return 0;
222 }
223 
224 
SetAttribute(const string & name,int value)225 void CNCBINode::SetAttribute(const string& name, int value)
226 {
227     SetAttribute(name, NStr::IntToString(value));
228 }
229 
230 
SetAttribute(const char * name,int value)231 void CNCBINode::SetAttribute(const char* name, int value)
232 {
233     SetAttribute(name, NStr::IntToString(value));
234 }
235 
236 
SetAttribute(const string & name)237 void CNCBINode::SetAttribute(const string& name)
238 {
239     DoSetAttribute(name, NcbiEmptyString, true);
240 }
241 
242 
DoSetAttribute(const string & name,const string & value,bool optional)243 void CNCBINode::DoSetAttribute(const string& name,
244                                const string& value, bool optional)
245 {
246     GetAttributes()[name] = SAttributeValue(value, optional);
247 }
248 
249 
SetAttributeOptional(const string & name,bool optional)250 void CNCBINode::SetAttributeOptional(const string& name, bool optional)
251 {
252     GetAttributes()[name].SetOptional(optional);
253 }
254 
255 
SetAttributeOptional(const char * name,bool optional)256 void CNCBINode::SetAttributeOptional(const char* name, bool optional)
257 {
258     SetAttributeOptional(string(name), optional);
259 }
260 
261 
SetAttribute(const char * name)262 void CNCBINode::SetAttribute(const char* name)
263 {
264     SetAttribute(string(name));
265 }
266 
267 
SetAttribute(const char * name,const string & value)268 void CNCBINode::SetAttribute(const char* name, const string& value)
269 {
270     SetAttribute(string(name), value);
271 }
272 
273 
MapTag(const string &)274 CNCBINode* CNCBINode::MapTag(const string& /*tagname*/)
275 {
276     return 0;
277 }
278 
279 
MapTagAll(const string & tagname,const TMode & mode)280 CNodeRef CNCBINode::MapTagAll(const string& tagname, const TMode& mode)
281 {
282     const TMode* context = &mode;
283     do {
284         CNCBINode* stackNode = context->GetNode();
285         if ( stackNode ) {
286             CNCBINode* mapNode = stackNode->MapTag(tagname);
287             if ( mapNode )
288                 return CNodeRef(mapNode);
289         }
290         context = context->GetPreviousContext();
291     } while ( context );
292     return CNodeRef(0);
293 }
294 
295 
Print(CNcbiOstream & out,TMode prev)296 CNcbiOstream& CNCBINode::Print(CNcbiOstream& out, TMode prev)
297 {
298     Initialize();
299     TMode mode(&prev, this);
300 
301     size_t n_count = GetRepeatCount();
302     for (size_t i = 0; i < n_count; i++ )
303     {
304         try {
305             PrintBegin(out, mode);
306             PrintChildren(out, mode);
307         }
308         catch (CHTMLException& e) {
309             e.AddTraceInfo(GetName());
310             throw;
311         }
312         catch (CException& e) {
313             TExceptionFlags flags = GetExceptionFlags();
314             if ( (flags  &  CNCBINode::fCatchAll) == 0 ) {
315                 throw;
316             }
317             CHTMLException new_e(DIAG_COMPILE_INFO, 0,
318                                  CHTMLException::eUnknown, e.GetMsg());
319             new_e.AddTraceInfo(GetName());
320             throw new_e;
321         }
322         catch (exception& e) {
323             TExceptionFlags flags = GetExceptionFlags();
324             if ( (flags  &  CNCBINode::fCatchAll) == 0 ) {
325                 throw;
326             }
327             CHTMLException new_e(DIAG_COMPILE_INFO, 0,
328                                  CHTMLException::eUnknown,
329                                  string("CNCBINode::Print: ") + e.what());
330             new_e.AddTraceInfo(GetName());
331             throw new_e;
332         }
333         catch (...) {
334             TExceptionFlags flags = GetExceptionFlags();
335             if ( (flags  &  CNCBINode::fCatchAll) == 0 ) {
336                 throw;
337             }
338             CHTMLException new_e(DIAG_COMPILE_INFO, 0,
339                                  CHTMLException::eUnknown,
340                                  "CNCBINode::Print: unknown exception");
341             new_e.AddTraceInfo(GetName());
342             throw new_e;
343         }
344         PrintEnd(out, mode);
345     }
346     return out;
347 }
348 
349 
PrintBegin(CNcbiOstream & out,TMode)350 CNcbiOstream& CNCBINode::PrintBegin(CNcbiOstream& out, TMode)
351 {
352     return out;
353 }
354 
355 
PrintEnd(CNcbiOstream & out,TMode)356 CNcbiOstream& CNCBINode::PrintEnd(CNcbiOstream& out, TMode)
357 {
358     return out;
359 }
360 
361 
PrintChildren(CNcbiOstream & out,TMode mode)362 CNcbiOstream& CNCBINode::PrintChildren(CNcbiOstream& out, TMode mode)
363 {
364     if ( HaveChildren() ) {
365         NON_CONST_ITERATE ( TChildren, i, Children() ) {
366             Node(i)->Print(out, mode);
367         }
368     }
369     return out;
370 }
371 
372 
Initialize(void)373 void CNCBINode::Initialize(void)
374 {
375     if ( !m_CreateSubNodesCalled ) {
376         m_CreateSubNodesCalled = true;
377         CreateSubNodes();
378     }
379 }
380 
381 
ReInitialize(void)382 void CNCBINode::ReInitialize(void)
383 {
384     RemoveAllChildren();
385     m_CreateSubNodesCalled = false;
386 }
387 
388 
CreateSubNodes(void)389 void CNCBINode::CreateSubNodes(void)
390 {
391     return;
392 }
393 
394 
SetExceptionFlags(TExceptionFlags flags)395 void CNCBINode::SetExceptionFlags(TExceptionFlags flags)
396 {
397     s_TlsExceptionFlags.SetValue(
398         reinterpret_cast<TExceptionFlags*> ((intptr_t) flags));
399 }
400 
401 
GetExceptionFlags()402 CNCBINode::TExceptionFlags CNCBINode::GetExceptionFlags()
403 {
404     // Some 64 bit compilers refuse to cast from int* to EExceptionFlags
405     return EExceptionFlags((intptr_t) s_TlsExceptionFlags.GetValue());
406 }
407 
408 
409 END_NCBI_SCOPE
410