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