1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: i2s_calculator.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
32 #include "i2s_calculator.hxx"
35 // NOT FULLY DEFINED SERVICES
38 #include <cosv/file.hxx>
39 //#include <adc_manager.hxx>
40 //#include <adc_options.hxx>
41 #include <ary/qualiname.hxx>
42 #include <ary/idl/i_enum.hxx>
43 #include <ary/idl/i_exception.hxx>
44 #include <ary/idl/i_function.hxx>
45 #include <ary/idl/i_gate.hxx>
46 #include <ary/idl/i_interface.hxx>
47 #include <ary/idl/ik_interface.hxx>
48 #include <ary/idl/i_module.hxx>
49 #include <ary/idl/i_property.hxx>
50 #include <ary/idl/i_service.hxx>
51 #include <ary/idl/i_singleton.hxx>
52 #include <ary/idl/i_siservice.hxx>
53 #include <ary/idl/i_sisingleton.hxx>
54 #include <ary/idl/i_struct.hxx>
55 #include <ary/idl/i_structelem.hxx>
56 #include <ary/idl/i_typedef.hxx>
57 #include <ary/idl/ip_ce.hxx>
58 #include <ary/idl/ip_type.hxx>
59 #include <ary/namesort.hxx>
60 #include <nametreenode.hxx>
61 #include "i_nnfinder.hxx"
63 #include "ia_type.hxx"
65 #include "is_type.hxx"
67 #include "it_explicit.hxx"
68 #include "it_sequence.hxx"
69 #include "it_xnameroom.hxx"
80 SecondariesCalculator::SearchCe4Type(Type_id i_type
)
83 ce
= lhf_Search_CeFromTypeId(i_type
);
85 return ary_cast
<DEST
>(& my_CeStorage()[ce
]);
90 typedef stg::const_iterator
<CodeEntity
> stg_citerator
;
91 typedef stg::iterator
<CodeEntity
> stg_iterator
;
93 typedef stg::filter_iterator
<CodeEntity
,Interface
>
96 typedef stg::filter_iterator
<Type
,ExplicitType
>
97 explicittype_iterator
;
99 typedef ary::stg::const_filter_iterator
<CodeEntity
,Typedef
>
104 SecondariesCalculator::lhf_SearchService( Type_id i_nType
)
106 return SearchCe4Type
<Service
>(i_nType
);
110 SecondariesCalculator::lhf_SearchInterface( Type_id i_nType
)
112 return SearchCe4Type
<Interface
>(i_nType
);
116 SecondariesCalculator::lhf_SearchStruct( Type_id i_nType
)
118 return SearchCe4Type
<Struct
>(i_nType
);
122 SecondariesCalculator::lhf_SearchException( Type_id i_nType
)
124 return SearchCe4Type
<Exception
>(i_nType
);
127 inline const Ce_Storage
&
128 SecondariesCalculator::my_CeStorage() const
130 csv_assert(pCes
!= 0);
131 return pCes
->Storage();
134 inline const Type_Storage
&
135 SecondariesCalculator::my_TypeStorage() const
137 csv_assert(pTypes
!= 0);
138 return pTypes
->Storage();
142 SecondariesCalculator::my_CeStorage()
144 csv_assert(pCes
!= 0);
145 return pCes
->Storage();
148 inline Type_Storage
&
149 SecondariesCalculator::my_TypeStorage()
151 csv_assert(pTypes
!= 0);
152 return pTypes
->Storage();
156 SecondariesCalculator::insert_into2sList( CodeEntity
& o_out
,
159 { o_out
.Secondaries().Access_List(i_listIndex
).push_back(i_nCe
); }
162 SecondariesCalculator::SecondariesCalculator( CeAdmin
& i_ces
,
163 TypeAdmin
& i_types
)
169 SecondariesCalculator::~SecondariesCalculator()
175 SecondariesCalculator::CheckAllInterfaceBases()
178 rGlobalNamespace
= pCes
->GlobalNamespace();
180 aXInterface("::com::sun::star::uno::XInterface","::");
183 rType
= pTypes
->CheckIn_Type( aXInterface
,
185 rGlobalNamespace
.CeId(),
188 nTypeXInterface
= rType
.TypeId();
190 rExplType
= ary_cast
<ExplicitType
>(rType
);
192 nCeXInterface
= lhf_Search_CeForType(rExplType
);
194 interface_iterator
itEnd( my_CeStorage().End() );
195 for ( interface_iterator
it( my_CeStorage().BeginUnreserved() );
199 if (NOT it
.IsValid())
204 if ( NOT rInterface
.HasBase() // According to UNO IDL syntax, an interface without base has com::sun::star::uno::XInterface as base.
205 AND rInterface
.CeId() != nCeXInterface
) // XInterface must not be base of itself.
207 rInterface
.Add_Base(nTypeXInterface
, 0);
213 SecondariesCalculator::Connect_Types2Ces()
215 explicittype_iterator
itEnd( my_TypeStorage().End() );
216 for ( explicittype_iterator
it( my_TypeStorage().BeginUnreserved() );
220 if (NOT it
.IsValid())
224 rType
= ary_cast
<ExplicitType
>(*it
);
226 nRelatedCe
= lhf_Search_CeForType(rType
);
227 if (nRelatedCe
.IsValid())
230 pNew
= new Ce_Type(nRelatedCe
, rType
.TemplateParameters());
231 my_TypeStorage().Replace_Entity( rType
.TypeId(),
238 SecondariesCalculator::Gather_CrossReferences()
242 for ( stg_iterator it
= my_CeStorage().Begin();
243 it
!= my_CeStorage().End();
246 (*it
).Accept( static_cast< SPInst_asHost
& >(*this) );
254 SecondariesCalculator::Make_Links2DeveloperManual(
255 const String
& i_devman_reffilepath
)
257 // const autodoc::Options &
258 // rOptions = TheAutodocManager().TheOptions();
261 // rDeveloperManual_URL
262 // = rOptions.Get_Extra(autodoc::OPT_developer_guide);
264 // rDeveloperManual_ReferenceFile
265 // = rOptions.Get_Extra(autodoc::OPT_developer_guide_refs_file);
267 // if ( rDeveloperManual_URL.length() == 0
269 // rDeveloperManual_ReferenceFile.length() == 0 )
275 aFile(i_devman_reffilepath
, csv::CFM_READ
);
280 Read_Links2DevManual(aFile
);
293 struct OrderCeIdsByName
296 const Ce_Storage
& i_storage
)
297 : rStorage(i_storage
),
303 return aNameComparison( rStorage
[i_ce1
].LocalName(),
304 rStorage
[i_ce2
].LocalName() );
308 const Ce_Storage
& rStorage
;
309 LesserName aNameComparison
;
318 SecondariesCalculator::do_Process( const Service
& i_rData
)
321 rService
= ary_cast
<Service
>(i_rData
);
324 assignImplementation_toAServicesInterfaces( rService
.CeId(),
326 interface_2s_ExportingServices
);
327 // Services and their interfaces:
328 recursive_AssignIncludingService(rService
.CeId(), rService
);
332 SecondariesCalculator::do_Process( const Interface
& i_rData
)
334 assign_AsDerivedInterface( ary_cast
<Interface
>(i_rData
) );
338 SecondariesCalculator::do_Process( const Struct
& i_rData
)
340 assign_AsDerivedStruct( ary_cast
<Struct
>(i_rData
) );
344 SecondariesCalculator::do_Process( const Exception
& i_rData
)
346 assign_AsDerivedException( ary_cast
<Exception
>(i_rData
) );
350 SecondariesCalculator::do_Process( const Typedef
& )
353 // Find out what was meant here ???
356 // rTypedef = ary_cast<Typedef>(i_rData);
360 SecondariesCalculator::do_Process( const Singleton
& i_rData
)
363 rSingleton
= ary_cast
<Singleton
>(i_rData
);
366 pServ
= lhf_SearchService(rSingleton
.AssociatedService());
369 insert_into2sUnique( *pServ
,
370 service_2s_InstantiatingSingletons
,
375 assignImplementation_toAServicesInterfaces( rSingleton
.CeId(),
376 lhf_Search_CeFromTypeId(rSingleton
.AssociatedService()),
377 interface_2s_ExportingSingletons
);
381 SecondariesCalculator::do_Process( const SglIfcService
& i_rData
)
383 const SglIfcService
&
384 rSglIfcService
= ary_cast
<SglIfcService
>(i_rData
);
386 assignImplementation_toAServicesInterfaces( rSglIfcService
.CeId(),
387 rSglIfcService
.CeId(),
388 interface_2s_ExportingServices
);
392 SecondariesCalculator::do_Process( const SglIfcSingleton
& i_rData
)
394 const SglIfcSingleton
&
395 rSglIfcSingleton
= ary_cast
<SglIfcSingleton
>(i_rData
);
397 Type_id nBase
= rSglIfcSingleton
.BaseInterface();
398 recursive_AssignImplementation_toExportedInterface( rSglIfcSingleton
.CeId(),
400 interface_2s_ExportingSingletons
);
404 SecondariesCalculator::do_Process( const Function
& i_rData
)
407 rFunction
= ary_cast
<Function
>(i_rData
);
409 recursive_AssignFunction_toCeAsReturn(rFunction
.CeId(), rFunction
.ReturnType());
411 for ( Function::ParamList::const_iterator itp
= rFunction
.Parameters().begin();
412 itp
!= rFunction
.Parameters().end();
415 recursive_AssignFunction_toCeAsParameter(rFunction
.CeId(), (*itp
).Type());
418 for ( Function::ExceptionList::const_iterator itx
= rFunction
.Exceptions().begin();
419 itx
!= rFunction
.Exceptions().end();
423 pX
= lhf_SearchException(*itx
);
426 insert_into2sUnique(*pX
, exception_2s_RaisingFunctions
, rFunction
.CeId());
432 SecondariesCalculator::do_Process( const StructElement
& i_rData
)
434 const StructElement
&
435 rStructElement
= ary_cast
<StructElement
>(i_rData
);
437 recursive_AssignStructElement_toCeAsDataType(rStructElement
.CeId(), rStructElement
.Type());
441 SecondariesCalculator::do_Process( const Property
& i_rData
)
444 rProperty
= ary_cast
<Property
>(i_rData
);
446 recursive_AssignStructElement_toCeAsDataType(rProperty
.CeId(), rProperty
.Type());
450 SecondariesCalculator::lhf_Search_CeForType( const ExplicitType
& i_rType
) const
452 const ExplicitNameRoom
&
453 rExplicitNameRoom
= ary_cast
<ExplicitNameRoom
>(
454 my_TypeStorage()[i_rType
.NameRoom()] );
456 rNodeFinder( my_CeStorage(),
457 rExplicitNameRoom
.NameChain_Begin(),
458 rExplicitNameRoom
.NameChain_End(),
461 if ( rExplicitNameRoom
.IsAbsolute() )
464 rGlobalNamespace
= ary_cast
<Module
>(
465 my_CeStorage()[predefined::ce_GlobalNamespace
]);
466 return Search_SubTree( rGlobalNamespace
,
472 rStartModule
= ary_cast
<Module
>(
473 my_CeStorage()[i_rType
.ModuleOfOccurrence()]);
474 Ce_id ret
= Search_SubTree_UpTillRoot( rStartModule
,
477 } // endif (rExplicitNameRoom.IsAbsolute()) else
481 SecondariesCalculator::lhf_Search_CeFromTypeId( Type_id i_nType
) const
483 if (NOT i_nType
.IsValid())
486 pType
= ary_cast
<Ce_Type
>( & my_TypeStorage()[i_nType
] );
493 SecondariesCalculator::assign_CurLink( char * i_text
,
494 const String
& i_link
,
495 const String
& i_linkUI
,
499 csv_assert(i_text
!= 0);
501 const ary::idl::Module
*
502 pModule
= & ary_cast
<Module
>(
503 my_CeStorage()[predefined::ce_GlobalNamespace
]);
505 char * pPastNext
= 0;
506 char * pNext
= i_text
;
508 (pPastNext
= strstr(pNext
,".")) != 0;
509 pNext
= pPastNext
+ 1 )
511 String
sNext(pNext
, pPastNext
-pNext
);
512 Ce_id nModule
= pModule
->Search_Name(sNext
);
513 if (nModule
.IsValid())
515 pModule
= ary_cast
<Module
>( & my_CeStorage()[nModule
] );
524 Cerr() << "Warning: Invalid line nr. "
526 << " in DevelopersGuide reference file:\n"
527 << reinterpret_cast< const char* >(i_text
)
534 pPastNext
= strchr(pNext
,':');
535 bool bMember
= pPastNext
!= 0;
536 String
sCe( pNext
, (bMember
? csv::str::size(pPastNext
-pNext
) : csv::str::maxsize
) );
539 // String sMember(bMember ? pPastNext+1, "");
541 Ce_id nCe
= pModule
->Search_Name(sCe
);
542 if (NOT nCe
.IsValid())
544 Cerr() << "Warning: Invalid line nr. "
546 << " in DevelopersGuide reference file:\n"
547 << reinterpret_cast< const char* >(i_text
)
554 rCe
= my_CeStorage()[nCe
];
558 rCe
.Secondaries().Add_Link2DescriptionInManual(i_link
, i_linkUI
);
560 rCe
.Secondaries().Add_Link2RefInManual(i_link
, i_linkUI
);
566 // Provisorial just doing nothing (or may be
567 // adding a link at main Ces lists).
569 // rCe.Secondaries().Add_Link2DescriptionInManual(i_link);
571 // rCe.Secondaries().Add_Link2RefInManual(i_link);
576 SecondariesCalculator::gather_Synonyms()
579 cstrg
= my_CeStorage();
580 typedef_citerator
itEnd(cstrg
.End());
581 for ( typedef_citerator
it(cstrg
.Begin());
585 if (NOT it
.IsValid())
590 recursive_AssignAsSynonym(rTypedef
.CeId(), rTypedef
);
595 SecondariesCalculator::recursive_AssignAsSynonym( Ce_id i_synonymousTypedefsId
,
596 const Typedef
& i_TypedefToCheck
)
599 nCe
= lhf_Search_CeFromTypeId(i_TypedefToCheck
.DefiningType());
600 if (NOT nCe
.IsValid())
603 rCe
= my_CeStorage()[nCe
];
605 switch (rCe
.AryClass()) // KORR_FUTURE: make this faster, remove switch.
607 case Interface::class_id
:
608 insert_into2sList( rCe
,
609 interface_2s_SynonymTypedefs
,
610 i_synonymousTypedefsId
);
612 case Struct::class_id
:
613 insert_into2sList( rCe
,
614 struct_2s_SynonymTypedefs
,
615 i_synonymousTypedefsId
);
618 insert_into2sList( rCe
,
619 enum_2s_SynonymTypedefs
,
620 i_synonymousTypedefsId
);
622 case Typedef::class_id
:
623 insert_into2sList( rCe
,
624 typedef_2s_SynonymTypedefs
,
625 i_synonymousTypedefsId
);
626 recursive_AssignAsSynonym( i_synonymousTypedefsId
,
627 static_cast< Typedef
& >(rCe
) );
629 // default: do nothing.
634 SecondariesCalculator::recursive_AssignIncludingService( Ce_id i_includingServicesId
,
635 const Service
& i_ServiceToCheckItsIncludes
)
637 Dyn_StdConstIterator
<CommentedRelation
>
639 i_ServiceToCheckItsIncludes
.Get_IncludedServices(pIncludedServices
);
641 for ( StdConstIterator
<CommentedRelation
> &
642 itServ
= *pIncludedServices
;
647 pServ
= lhf_SearchService((*itServ
).Type());
650 insert_into2sUnique( *pServ
,
651 service_2s_IncludingServices
,
652 i_includingServicesId
654 recursive_AssignIncludingService(i_includingServicesId
, *pServ
);
658 assignImplementation_toAServicesInterfaces( i_includingServicesId
,
659 lhf_Search_CeFromTypeId( (*itServ
).Type() ),
660 interface_2s_ExportingServices
);
665 SecondariesCalculator::assign_AsDerivedInterface( const Interface
& i_rDerived
)
667 ary::Dyn_StdConstIterator
<ary::idl::CommentedRelation
>
669 ary::idl::ifc_interface::attr::Get_Bases(pHelp
, i_rDerived
);
671 for ( ary::StdConstIterator
<ary::idl::CommentedRelation
> & it
= *pHelp
;
676 pIfc
= lhf_SearchInterface( (*it
).Type() );
680 insert_into2sList( *pIfc
,
681 interface_2s_Derivations
,
687 SecondariesCalculator::assign_AsDerivedStruct( const Struct
& i_rDerived
)
690 nBase
= i_rDerived
.Base();
694 pParent
= lhf_SearchStruct(nBase
);
697 insert_into2sList( *pParent
,
698 struct_2s_Derivations
,
705 SecondariesCalculator::assign_AsDerivedException( const Exception
& i_rDerived
)
708 nBase
= i_rDerived
.Base();
712 pParent
= lhf_SearchException(nBase
);
715 insert_into2sList( *pParent
,
716 exception_2s_Derivations
,
723 SecondariesCalculator::assignImplementation_toAServicesInterfaces(
726 E_2s_of_Interface i_eList
)
728 if (NOT i_nService
.IsValid())
731 pService
= ary_cast
<Service
>( & my_CeStorage()[i_nService
] );
733 pSglIfcService
= ary_cast
<SglIfcService
>( & my_CeStorage()[i_nService
] );
737 Dyn_StdConstIterator
<CommentedRelation
>
738 pSupportedInterfaces
;
739 pService
->Get_SupportedInterfaces(pSupportedInterfaces
);
741 for ( StdConstIterator
<CommentedRelation
> &
742 itInfc
= *pSupportedInterfaces
;
743 itInfc
.operator bool();
746 recursive_AssignImplementation_toExportedInterface( i_nImpl
,
751 else if (pSglIfcService
!= 0)
753 Type_id nBase
= pSglIfcService
->BaseInterface();
754 recursive_AssignImplementation_toExportedInterface( i_nImpl
,
761 SecondariesCalculator::recursive_AssignImplementation_toExportedInterface(
763 Type_id i_nExportedInterface
,
764 E_2s_of_Interface i_eList
)
767 pIfc
= lhf_SearchInterface(i_nExportedInterface
);
771 insert_into2sUnique( *pIfc
,
774 Dyn_StdConstIterator
<CommentedRelation
>
776 ary::idl::ifc_interface::attr::Get_Bases(pBases
, *pIfc
);
777 for ( StdConstIterator
<CommentedRelation
> & it
= *pBases
;
781 recursive_AssignImplementation_toExportedInterface(i_nService
, (*it
).Type(), i_eList
);
786 SecondariesCalculator::recursive_AssignFunction_toCeAsReturn( Ce_id i_nFunction
,
787 Type_id i_nReturnType
)
790 nCe
= lhf_Search_CeFromTypeId(i_nReturnType
);
791 if (NOT nCe
.IsValid())
795 rCe
= my_CeStorage()[nCe
];
796 switch (rCe
.AryClass()) // KORR_FUTURE: make this faster, remove switch.
798 case Interface::class_id
:
799 insert_into2sList( rCe
,
800 interface_2s_AsReturns
,
803 case Struct::class_id
:
804 insert_into2sList( rCe
,
809 insert_into2sList( rCe
,
813 case Typedef::class_id
:
814 insert_into2sList( rCe
,
815 typedef_2s_AsReturns
,
817 recursive_AssignFunction_toCeAsReturn( i_nFunction
,
818 static_cast< Typedef
& >(rCe
).DefiningType() );
820 // default: do nothing.
825 SecondariesCalculator::recursive_AssignFunction_toCeAsParameter( Ce_id i_nFunction
,
826 Type_id i_nParameterType
)
829 nCe
= lhf_Search_CeFromTypeId(i_nParameterType
);
830 if (NOT nCe
.IsValid())
834 rCe
= my_CeStorage()[nCe
];
835 switch (rCe
.AryClass()) // KORR_FUTURE: make this faster, remove switch.
837 case Interface::class_id
:
838 insert_into2sList( rCe
,
839 interface_2s_AsParameters
,
842 case Struct::class_id
:
843 insert_into2sList( rCe
,
844 struct_2s_AsParameters
,
848 insert_into2sList( rCe
,
849 enum_2s_AsParameters
,
852 case Typedef::class_id
:
853 insert_into2sList( rCe
,
854 typedef_2s_AsParameters
,
856 recursive_AssignFunction_toCeAsParameter( i_nFunction
,
857 static_cast< Typedef
& >(rCe
).DefiningType() );
859 // default: do nothing.
864 SecondariesCalculator::recursive_AssignStructElement_toCeAsDataType( Ce_id i_nDataElement
,
865 Type_id i_nDataType
)
868 nCe
= lhf_Search_CeFromTypeId(i_nDataType
);
869 if (NOT nCe
.IsValid())
873 rCe
= my_CeStorage()[nCe
];
874 switch (rCe
.AryClass()) // KORR_FUTURE: make this faster, remove switch.
876 case Interface::class_id
:
877 insert_into2sList( rCe
,
878 interface_2s_AsDataTypes
,
881 case Struct::class_id
:
882 insert_into2sList( rCe
,
883 struct_2s_AsDataTypes
,
887 insert_into2sList( rCe
,
891 case Typedef::class_id
:
892 insert_into2sList( rCe
,
893 typedef_2s_AsDataTypes
,
895 recursive_AssignFunction_toCeAsParameter( i_nDataElement
,
896 static_cast< Typedef
& >(rCe
).DefiningType() );
898 // default: do nothing.
903 SecondariesCalculator::insert_into2sUnique( CodeEntity
& o_out
,
908 rOut
= o_out
.Secondaries().Access_List(i_listIndex
);
909 if (std::find(rOut
.begin(),rOut
.end(),i_nCe
) != rOut
.end())
911 rOut
.push_back(i_nCe
);
915 SecondariesCalculator::sort_All2s()
918 aIdOrdering(my_CeStorage());
920 for ( stg_iterator it
= my_CeStorage().Begin();
921 it
!= my_CeStorage().End();
925 r2s
= (*it
).Secondaries();
926 int iCount
= r2s
.CountXrefLists();
927 for (int i
= 0; i
< iCount
; ++i
)
929 std::sort( r2s
.Access_List(i
).begin(),
930 r2s
.Access_List(i
).end(),
937 SecondariesCalculator::Read_Links2DevManual( csv::bstream
& i_file
)
939 StreamLock
aLine(300);
940 StreamStr
& rLine
= aLine();
945 E_LinkMode eCurMode
= link2ref
;
948 const char * sLink
= "LINK:";
949 const char * sDescr
= "DESCR:";
950 const char * sTopic
= "TOPIC:";
951 const char * sRef
= "REF:";
952 const UINT8 cMaxASCIINumWhiteSpace
= 32;
954 while (NOT i_file
.eod())
959 rLine
.operator_read_line(i_file
);
961 if ( *rLine
.c_str() >= 'a' )
963 assign_CurLink(rLine
.begin(), sCurLink
, sCurLinkUI
, eCurMode
== link2descr
, lineCount
);
965 else if ( strncmp(rLine
.c_str(), sLink
, strlen(sLink
)) == 0 )
967 sCurLink
= rLine
.c_str()+5;
970 else if ( strncmp(rLine
.c_str(), sDescr
, strlen(sDescr
)) == 0 )
972 sCurLinkUI
= rLine
.c_str()+6;
974 else if ( strncmp(rLine
.c_str(), sTopic
, strlen(sTopic
)) == 0 )
976 eCurMode
= link2descr
;
978 else if ( strncmp(rLine
.c_str(), sRef
, strlen(sRef
)) == 0 )
982 else if (static_cast<UINT8
>(*rLine
.c_str()) > cMaxASCIINumWhiteSpace
)
984 assign_CurLink(rLine
.begin(), sCurLink
, sCurLinkUI
, eCurMode
== link2descr
, lineCount
);
987 // Ignore empty line.