1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include <precomp.h>
29 #include <s2_dsapi/docu_pe2.hxx>
30 
31 
32 // NOT FULLY DEFINED SERVICES
33 #include <cctype>
34 #include <ary/doc/d_oldidldocu.hxx>
35 #include <ary_i/d_token.hxx>
36 #include <parser/parserinfo.hxx>
37 #include <adc_cl.hxx>
38 #include <adc_msg.hxx>
39 #include <../parser/inc/x_docu.hxx>
40 #include <s2_dsapi/dsapitok.hxx>
41 #include <s2_dsapi/tk_atag2.hxx>
42 #include <s2_dsapi/tk_html.hxx>
43 #include <s2_dsapi/tk_docw2.hxx>
44 #include <s2_dsapi/tk_xml.hxx>
45 
46 
47 #ifdef UNX
48 #define strnicmp strncasecmp
49 #endif
50 
51 
52 namespace csi
53 {
54 namespace dsapi
55 {
56 
57 
58 const char *		AtTagTitle(
59 						const Tok_AtTag & 	i_rToken );
60 
61 
62 SapiDocu_PE::SapiDocu_PE(ParserInfo & io_rPositionInfo)
63 	:	pDocu(0),
64 		eState(e_none),
65 		pPositionInfo(&io_rPositionInfo),
66 		fCurTokenAddFunction(&SapiDocu_PE::AddDocuToken2Void),
67 		pCurAtTag(0),
68 		sCurDimAttribute(),
69 		sCurAtSeeType_byXML(200)
70 {
71 }
72 
73 SapiDocu_PE::~SapiDocu_PE()
74 {
75 }
76 
77 void
78 SapiDocu_PE::ProcessToken( DYN csi::dsapi::Token & let_drToken )
79 {
80 	if (IsComplete())
81 	{
82 		pDocu = 0;
83 		eState = e_none;
84 	}
85 
86 	if ( eState == e_none )
87 	{
88 		pDocu = new ary::doc::OldIdlDocu;
89 		eState = st_short;
90 		fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2Short;
91 	}
92 
93 	csv_assert(pDocu);
94 
95 	let_drToken.Trigger(*this);
96 	delete &let_drToken;
97 }
98 
99 void
100 SapiDocu_PE::Process_AtTag(	const Tok_AtTag & i_rToken )
101 {
102 	if (NOT pCurAtTag)
103 	{
104 		eState = st_attags;
105 		fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2CurAtTag;
106 	}
107 	else
108 	{
109 		csv_assert(eState == st_attags);
110 		pDocu->AddAtTag(*pCurAtTag.Release());
111 	}
112 
113 	if (i_rToken.Id() == Tok_AtTag::param)
114 	{
115 		pCurAtTag = new DT_ParameterAtTag;
116 		fCurTokenAddFunction = &SapiDocu_PE::SetCurParameterAtTagName;
117 	}
118 	else if (i_rToken.Id() == Tok_AtTag::see)
119 	{
120 		pCurAtTag = new DT_SeeAlsoAtTag;
121 		fCurTokenAddFunction = &SapiDocu_PE::SetCurSeeAlsoAtTagLinkText;
122 	}
123 	else if (i_rToken.Id() == Tok_AtTag::deprecated)
124 	{
125     	pDocu->SetDeprecated();
126 		pCurAtTag = new DT_StdAtTag("");    // Dummy that will not be used.
127 		fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2Deprecated;
128 	}
129 	else if (i_rToken.Id() == Tok_AtTag::since)
130 	{
131 		pCurAtTag = new DT_SinceAtTag;
132 		fCurTokenAddFunction = &SapiDocu_PE::SetCurSinceAtTagVersion_OOo;
133 	}
134 	else
135 	{
136 		pCurAtTag = new DT_StdAtTag( AtTagTitle(i_rToken) );
137 		fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2CurAtTag;
138 	}
139 }
140 
141 void
142 SapiDocu_PE::Process_HtmlTag( const Tok_HtmlTag & i_rToken )
143 {
144 	if (eState == st_short AND i_rToken.IsParagraphStarter())
145 	{
146 		eState = st_description;
147         fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2Description;
148 	}
149 
150 	// Workaround special for some errors in API docu:
151 	if ( strnicmp("<true",i_rToken.Text(),5 ) == 0 )
152 	{
153 	    if ( strcmp("<TRUE/>",i_rToken.Text()) != 0 )
154             TheMessages().Out_InvalidConstSymbol( i_rToken.Text(),
155                                               pPositionInfo->CurFile(),
156                                               pPositionInfo->CurLine() );
157 		(this->*fCurTokenAddFunction)( *new DT_TextToken("<b>true</b>") );
158 		return;
159 	}
160 	else if ( strnicmp("<false",i_rToken.Text(),6 ) == 0 )
161 	{
162 	    if ( strcmp("<FALSE/>",i_rToken.Text()) != 0 )
163             TheMessages().Out_InvalidConstSymbol( i_rToken.Text(),
164                                               pPositionInfo->CurFile(),
165                                               pPositionInfo->CurLine() );
166 		(this->*fCurTokenAddFunction)( *new DT_TextToken("<b>false</b>") );
167 		return;
168 	}
169 	else if ( strnicmp("<NULL",i_rToken.Text(),5 ) == 0 )
170 	{
171 	    if ( strcmp("<NULL/>",i_rToken.Text()) != 0 )
172             TheMessages().Out_InvalidConstSymbol( i_rToken.Text(),
173                                               pPositionInfo->CurFile(),
174                                               pPositionInfo->CurLine() );
175 		(this->*fCurTokenAddFunction)( *new DT_TextToken("<b>null</b>") );
176 		return;
177 	}
178 	else if ( strnicmp("<void",i_rToken.Text(),5 ) == 0 )
179 	{
180 	    if ( strcmp("<void/>",i_rToken.Text()) != 0 )
181             TheMessages().Out_InvalidConstSymbol( i_rToken.Text(),
182                                               pPositionInfo->CurFile(),
183                                               pPositionInfo->CurLine() );
184 		(this->*fCurTokenAddFunction)( *new DT_TextToken("<b>void</b>") );
185 		return;
186 	}
187 
188 	(this->*fCurTokenAddFunction)( *new DT_Style(i_rToken.Text(),false) );
189 }
190 
191 void
192 SapiDocu_PE::Process_XmlConst( const Tok_XmlConst & i_rToken )
193 {
194 	(this->*fCurTokenAddFunction)(*new DT_MupConst(i_rToken.Text()));
195 }
196 
197 void
198 SapiDocu_PE::Process_XmlLink_BeginTag( const Tok_XmlLink_BeginTag & i_rToken )
199 {
200 	switch (i_rToken.Id())
201 	{
202 		case Tok_XmlLink_Tag::e_const:
203 			(this->*fCurTokenAddFunction)(*new DT_Style("<b>",false));
204 			break;
205 		case Tok_XmlLink_Tag::member:
206 			(this->*fCurTokenAddFunction)(*new DT_MupMember(i_rToken.Scope()));
207 			break;
208 		case Tok_XmlLink_Tag::type:
209 			(this->*fCurTokenAddFunction)(*new DT_MupType(i_rToken.Scope()));
210 			break;
211 		default:
212 		    //  Do nothing.
213 		    ;
214 	}
215 
216     if ( i_rToken.Dim().length() > 0 )
217         sCurDimAttribute = i_rToken.Dim();
218     else
219         sCurDimAttribute.clear();
220 }
221 
222 void
223 SapiDocu_PE::Process_XmlLink_EndTag( const Tok_XmlLink_EndTag & i_rToken )
224 {
225 	switch (i_rToken.Id())
226 	{
227 		case Tok_XmlLink_Tag::e_const:
228 			(this->*fCurTokenAddFunction)(*new DT_Style("</b>",false));
229 			break;
230 		case Tok_XmlLink_Tag::member:
231 			(this->*fCurTokenAddFunction)(*new DT_MupMember(true));
232 			break;
233 		case Tok_XmlLink_Tag::type:
234 			(this->*fCurTokenAddFunction)(*new DT_MupType(true));
235 			break;
236 		default:
237 		    //  Do nothing.
238 		    ;
239 	}
240     if ( sCurDimAttribute.length() > 0 )
241     {
242         (this->*fCurTokenAddFunction)( *new DT_TextToken(sCurDimAttribute.c_str()) );
243         sCurDimAttribute.clear();
244     }
245 }
246 
247 void
248 SapiDocu_PE::Process_XmlFormat_BeginTag( const Tok_XmlFormat_BeginTag & i_rToken )
249 {
250 	switch (i_rToken.Id())
251 	{
252 		case Tok_XmlFormat_Tag::code:
253 			(this->*fCurTokenAddFunction)(*new DT_Style("<code>",false));
254 			break;
255 		case Tok_XmlFormat_Tag::listing:
256 			(this->*fCurTokenAddFunction)(*new DT_Style("<pre>",true));
257 			break;
258 		case Tok_XmlFormat_Tag::atom:
259 			(this->*fCurTokenAddFunction)(*new DT_Style("<code>",true));
260 			break;
261 		default:
262 		    //  Do nothing.
263 		    ;
264 	}
265     if ( i_rToken.Dim().length() > 0 )
266         sCurDimAttribute = i_rToken.Dim();
267     else
268         sCurDimAttribute.clear();
269 }
270 
271 void
272 SapiDocu_PE::Process_XmlFormat_EndTag( const Tok_XmlFormat_EndTag & i_rToken )
273 {
274 	switch (i_rToken.Id())
275 	{
276 		case Tok_XmlFormat_Tag::code:
277 			(this->*fCurTokenAddFunction)(*new DT_Style("</code>",false));
278 			break;
279 		case Tok_XmlFormat_Tag::listing:
280 			(this->*fCurTokenAddFunction)(*new DT_Style("</pre>",true));
281 			break;
282 		case Tok_XmlFormat_Tag::atom:
283 			(this->*fCurTokenAddFunction)(*new DT_Style("</code>",true));
284 			break;
285 		default:
286 		    //  Do nothing.
287 		    ;
288 	}
289     if ( sCurDimAttribute.length() > 0 )
290     {
291         (this->*fCurTokenAddFunction)( *new DT_TextToken(sCurDimAttribute.c_str()) );
292         sCurDimAttribute.clear();
293     }
294 }
295 
296 void
297 SapiDocu_PE::Process_Word( const Tok_Word &	i_rToken )
298 {
299 	(this->*fCurTokenAddFunction)(*new DT_TextToken(i_rToken.Text()));
300 }
301 
302 void
303 SapiDocu_PE::Process_Comma()
304 {
305 	csv_assert(1==7);
306 //	(this->*fCurTokenAddFunction)(*new DT_Comma(i_rToken.Text()));
307 }
308 
309 void
310 SapiDocu_PE::Process_DocuEnd()
311 {
312 	eState = st_complete;
313 	if (pCurAtTag)
314 		pDocu->AddAtTag(*pCurAtTag.Release());
315 	fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2Void;
316 }
317 
318 void
319 SapiDocu_PE::Process_EOL()
320 {
321 	(this->*fCurTokenAddFunction)(*new DT_EOL);
322 }
323 
324 void
325 SapiDocu_PE::Process_White()
326 {
327 	(this->*fCurTokenAddFunction)(*new DT_White);
328 }
329 
330 DYN ary::doc::OldIdlDocu *
331 SapiDocu_PE::ReleaseJustParsedDocu()
332 {
333 	if (IsComplete())
334 	{
335 		eState = e_none;
336 		return pDocu.Release();
337 	}
338 	return 0;
339 }
340 
341 
342 bool
343 SapiDocu_PE::IsComplete() const
344 {
345 	return eState == st_complete;
346 }
347 
348 void
349 SapiDocu_PE::AddDocuToken2Void( DYN ary::inf::DocuToken & let_drNewToken )
350 {
351 	delete &let_drNewToken;
352 }
353 
354 void
355 SapiDocu_PE::AddDocuToken2Short( DYN ary::inf::DocuToken & let_drNewToken )
356 {
357 	csv_assert(pDocu);
358 	pDocu->AddToken2Short(let_drNewToken);
359 }
360 
361 void
362 SapiDocu_PE::AddDocuToken2Description( DYN ary::inf::DocuToken & let_drNewToken )
363 {
364 	csv_assert(pDocu);
365 	pDocu->AddToken2Description(let_drNewToken);
366 }
367 
368 void
369 SapiDocu_PE::AddDocuToken2Deprecated( DYN ary::inf::DocuToken & let_drNewToken )
370 {
371 	csv_assert(pDocu);
372 	pDocu->AddToken2DeprecatedText(let_drNewToken);
373 }
374 
375 void
376 SapiDocu_PE::AddDocuToken2CurAtTag( DYN ary::inf::DocuToken & let_drNewToken )
377 {
378 	csv_assert(pCurAtTag);
379 	pCurAtTag->AddToken(let_drNewToken);
380 }
381 
382 void
383 SapiDocu_PE::SetCurParameterAtTagName( DYN ary::inf::DocuToken & let_drNewToken )
384 {
385     if (let_drNewToken.IsWhiteOnly())
386     {
387         delete &let_drNewToken;
388         return;
389     }
390 
391 	csv_assert(pCurAtTag);
392 	DT_TextToken * dpText = dynamic_cast< DT_TextToken* >(&let_drNewToken);
393 	if (dpText != 0)
394 		pCurAtTag->SetName(dpText->GetText());
395 	else
396 		pCurAtTag->SetName("parameter ?");
397 	delete &let_drNewToken;
398 	fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2CurAtTag;
399 }
400 
401 void
402 SapiDocu_PE::SetCurSeeAlsoAtTagLinkText( DYN ary::inf::DocuToken & let_drNewToken )
403 {
404 	csv_assert(pCurAtTag);
405 
406     if (let_drNewToken.IsWhiteOnly())
407     {
408         delete &let_drNewToken;
409         return;
410     }
411 
412 	DT_TextToken * pText = dynamic_cast< DT_TextToken* >(&let_drNewToken);
413 	if (pText != 0)
414 		pCurAtTag->SetName(pText->GetText());
415     else
416     {
417     	DT_MupType *
418     	    pTypeBegin = dynamic_cast< DT_MupType* >(&let_drNewToken);
419     	DT_MupMember *
420     	    pMemberBegin = dynamic_cast< DT_MupMember* >(&let_drNewToken);
421         if (pTypeBegin != 0 OR pMemberBegin != 0)
422         {
423             sCurAtSeeType_byXML.reset();
424 
425             sCurAtSeeType_byXML
426                 << ( pTypeBegin != 0
427                         ?   pTypeBegin->Scope()
428                         :   pMemberBegin->Scope() );
429 
430             if (sCurAtSeeType_byXML.tellp() > 0)
431             {
432                 sCurAtSeeType_byXML
433                     << "::";
434             }
435         	delete &let_drNewToken;
436         	fCurTokenAddFunction = &SapiDocu_PE::SetCurSeeAlsoAtTagLinkText_2;
437         	return;
438         }
439         else
440         {
441     		pCurAtTag->SetName("? (no identifier found)");
442         }
443     }
444 	delete &let_drNewToken;
445 	fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2CurAtTag;
446 }
447 
448 void
449 SapiDocu_PE::SetCurSeeAlsoAtTagLinkText_2( DYN ary::inf::DocuToken & let_drNewToken )
450 {
451 	csv_assert(pCurAtTag);
452 
453     if (let_drNewToken.IsWhiteOnly())
454     {
455         delete &let_drNewToken;
456         return;
457     }
458 
459 	DT_TextToken *
460 	    pText = dynamic_cast< DT_TextToken* >(&let_drNewToken);
461 	if (pText != 0)
462 	{
463 		sCurAtSeeType_byXML
464 		    << pText->GetText();
465 		pCurAtTag->SetName(sCurAtSeeType_byXML.c_str());
466 	}
467     else
468     {
469         pCurAtTag->SetName("? (no identifier found)");
470     }
471 	sCurAtSeeType_byXML.reset();
472 	delete &let_drNewToken;
473 	fCurTokenAddFunction = &SapiDocu_PE::SetCurSeeAlsoAtTagLinkText_3;
474 }
475 
476 void
477 SapiDocu_PE::SetCurSeeAlsoAtTagLinkText_3( DYN ary::inf::DocuToken & let_drNewToken )
478 {
479 	csv_assert(pCurAtTag);
480 
481     if (let_drNewToken.IsWhiteOnly())
482     {
483         delete &let_drNewToken;
484         return;
485     }
486 
487     /// Could emit warning, but don't because this parser is obsolete.
488 //	Tok_XmlLink_BeginTag *
489 //	    pLinkEnd = dynamic_cast< Tok_XmlLink_EndTag* >(&let_drNewToken);
490 //	if (pLinkEnd == 0)
491 //	{
492 //	    warn_aboutMissingClosingTag();
493 //	}
494 
495 	delete &let_drNewToken;
496 	fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2CurAtTag;
497 }
498 
499 const String
500     C_sSinceFormat("Correct version format: \"OOo <major>.<minor>[.<micro> if micro is not 0]\".");
501 
502 void
503 SapiDocu_PE::SetCurSinceAtTagVersion_OOo( DYN ary::inf::DocuToken & let_drNewToken )
504 {
505 	csv_assert(pCurAtTag);
506 
507     DT_TextToken * pToken = dynamic_cast< DT_TextToken* >(&let_drNewToken);
508     if (pToken == 0)
509     {
510     	delete &let_drNewToken;
511         return;
512     }
513 
514     const String
515         sVersion(pToken->GetText());
516     if (NOT CheckVersionSyntax_OOo(sVersion))
517     {
518         Cerr() << "Version information in @since tag has incorrect format.\n"
519                << "Found: \"" << sVersion << "\"\n"
520                << C_sSinceFormat
521                << Endl();
522         exit(1);
523     }
524 
525     const autodoc::CommandLine &
526         rCommandLine = autodoc::CommandLine::Get_();
527     if (NOT rCommandLine.DoesTransform_SinceTag())
528         pCurAtTag->AddToken(let_drNewToken);
529 
530     fCurTokenAddFunction = &SapiDocu_PE::SetCurSinceAtTagVersion_Number;
531 }
532 
533 void
534 SapiDocu_PE::SetCurSinceAtTagVersion_Number( DYN ary::inf::DocuToken & let_drNewToken )
535 {
536 	csv_assert(pCurAtTag);
537 
538     DT_TextToken * pToken = dynamic_cast< DT_TextToken* >(&let_drNewToken);
539     if (pToken == 0)
540     {
541         if (dynamic_cast< DT_White* >(&let_drNewToken) != 0)
542         {
543             String &
544                 sValue = pCurAtTag->Access_Text().Access_TextOfFirstToken();
545             StreamLock
546                 sHelp(1000);
547             sValue = sHelp() << sValue << " " << c_str;
548         }
549 
550     	delete &let_drNewToken;
551         return;
552     }
553 
554     const String
555         sVersion(pToken->GetText());
556     if (NOT CheckVersionSyntax_Number(sVersion))
557     {
558         Cerr() << "Version information in @since tag has incorrect format.\n"
559                << "Found: \"" << sVersion << "\"\n"
560                << C_sSinceFormat
561                << Endl();
562         exit(1);
563     }
564 
565     const autodoc::CommandLine &
566         rCommandLine = autodoc::CommandLine::Get_();
567     if ( rCommandLine.DoesTransform_SinceTag())
568     {
569         pCurAtTag->AddToken(let_drNewToken);
570 
571         if (rCommandLine.DisplayOf_SinceTagValue(sVersion).empty())
572         {
573             // This is the numbered part, but we don't know it.
574         	delete &let_drNewToken;
575 
576             StreamLock
577                 sl(200);
578             sl()
579                 << "Since-value '"
580                 << sVersion
581                 << "' not found in translation table.";
582             throw X_Docu("since", sl().c_str());
583         }
584     }
585     else
586     {
587         AddDocuToken2SinceAtTag(let_drNewToken);
588     }
589     fCurTokenAddFunction = &SapiDocu_PE::AddDocuToken2SinceAtTag;
590 }
591 
592 void
593 SapiDocu_PE::AddDocuToken2SinceAtTag( DYN ary::inf::DocuToken & let_drNewToken )
594 {
595 	csv_assert(pCurAtTag);
596     String &
597         sValue = pCurAtTag->Access_Text().Access_TextOfFirstToken();
598     StreamLock
599         sHelp(1000);
600 
601     DT_TextToken *
602         pToken = dynamic_cast< DT_TextToken* >(&let_drNewToken);
603     if (pToken != 0)
604     {
605         sValue = sHelp() << sValue << pToken->GetText() << c_str;
606     }
607     else if (dynamic_cast< DT_White* >(&let_drNewToken) != 0)
608     {
609         sValue = sHelp() << sValue << " " << c_str;
610     }
611   	delete &let_drNewToken;
612 }
613 
614 bool
615 SapiDocu_PE::CheckVersionSyntax_OOo(const String & i_versionPart1)
616 {
617     return      i_versionPart1 == "OOo"
618             OR  i_versionPart1 == "OpenOffice.org";
619 }
620 
621 bool
622 SapiDocu_PE::CheckVersionSyntax_Number(const String & i_versionPart2)
623 {
624     if (i_versionPart2.length () == 0)
625         return false;
626 
627     const char
628         pt = '.';
629     unsigned int countDigit = 0;
630     unsigned int countPoint = 0;
631     const char *
632         pFirstPoint = 0;
633     const char *
634         pLastPoint = 0;
635 
636     for ( const char * p = i_versionPart2.begin();
637           *p != 0;
638           ++p )
639     {
640         if ( std::isdigit(*p) )
641             ++countDigit;
642         else if (*p == pt)
643         {
644             if (countPoint == 0)
645                 pFirstPoint = p;
646             pLastPoint = p;
647             ++countPoint;
648         }
649     }
650 
651     if (    countDigit + countPoint == i_versionPart2.length()         // only digits and points
652         AND pFirstPoint != 0 AND countPoint < 3                         // 1 or 2 points
653         AND pFirstPoint + 1 != pLastPoint                               // there are digits between two points
654         AND *i_versionPart2.begin() != pt AND *(pLastPoint + 1) != 0    // points are surrounded by digits
655         AND (*(pLastPoint + 1) != '0' OR pLastPoint == pFirstPoint) )   // the first micro-digit is not 0
656     {
657         return true;
658     }
659     return false;
660 }
661 
662 const char *
663 AtTagTitle( const Tok_AtTag & i_rToken )
664 {
665 	switch (i_rToken.Id())
666 	{
667 		case Tok_AtTag::author:		return "";
668 		case Tok_AtTag::see:	    return "See also";
669 		case Tok_AtTag::param:	    return "Parameters";
670 		case Tok_AtTag::e_return:	return "Returns";
671 		case Tok_AtTag::e_throw:	return "Throws";
672 		case Tok_AtTag::example:	return "Example";
673 		case Tok_AtTag::deprecated:	return "Deprecated";
674 		case Tok_AtTag::suspicious:	return "";
675 		case Tok_AtTag::missing:	return "";
676 		case Tok_AtTag::incomplete:	return "";
677 		case Tok_AtTag::version:	return "";
678 		case Tok_AtTag::guarantees:	return "Guarantees";
679 		case Tok_AtTag::exception:	return "Exception";
680 		case Tok_AtTag::since:	    return "Since version";
681 		default:
682 		    //  See below.
683 		    ;
684 	}
685 	return i_rToken.Text();
686 }
687 
688 
689 
690 }   // namespace dsapi
691 }   // namespace csi
692 
693