/***************************************************************************
* Copyright ( C ) 2004 - 2009 by Thomas Fischer *
* fischer @ unix - ag . uni - kl . de *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the *
* Free Software Foundation , Inc . , *
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "file.h"
# include "element.h"
# include "entry.h"
# include "macro.h"
# include "preamble.h"
# include "value.h"
# include "comment.h"
# include "encoderlatex.h"
# include "fileexporterbibtex.h"
namespace BibTeX
{
FileExporterBibTeX : : FileExporterBibTeX ( ) : FileExporter ( ) ,
m_iconvBufferSize ( 16384 ) , m_stringOpenDelimiter ( ' " ' ) , m_stringCloseDelimiter ( ' " ' ) , m_keywordCasing ( kcCamelCase ) , m_encoding ( " latex " ) , m_protectCasing ( FALSE ) , cancelFlag ( FALSE )
{
m_iconvBuffer = new char [ m_iconvBufferSize ] ;
}
FileExporterBibTeX : : ~ FileExporterBibTeX ( )
{
delete [ ] m_iconvBuffer ;
}
bool FileExporterBibTeX : : save ( TQIODevice * iodevice , const File * bibtexfile , TQStringList * /*errorLog*/ )
{
m_mutex . lock ( ) ;
bool result = TRUE ;
/**
* Categorize elements from the bib file into four groups ,
* to ensure that BibTeX finds all connected elements
* in the correct order .
*/
TQValueList < Comment * > parameterCommentsList ;
TQValueList < Preamble * > preambleList ;
TQValueList < Macro * > macroList ;
TQValueList < Entry * > crossRefingEntryList ;
TQValueList < Element * > remainingList ;
for ( File : : ElementList : : const_iterator it = bibtexfile - > elements . begin ( ) ; it ! = bibtexfile - > elements . end ( ) & & result & & ! cancelFlag ; it + + )
{
Preamble * preamble = dynamic_cast < Preamble * > ( * it ) ;
if ( preamble ! = NULL )
preambleList . append ( preamble ) ;
else
{
Macro * macro = dynamic_cast < Macro * > ( * it ) ;
if ( macro ! = NULL )
macroList . append ( macro ) ;
else
{
Entry * entry = dynamic_cast < Entry * > ( * it ) ;
if ( ( entry ! = NULL ) & & ( entry - > getField ( EntryField : : ftCrossRef ) ! = NULL ) )
crossRefingEntryList . append ( entry ) ;
else
{
Comment * comment = dynamic_cast < Comment * > ( * it ) ;
TQString commentText = TQString : : null ;
/** check if this file requests a special encoding */
if ( comment ! = NULL & & comment - > useCommand ( ) & & ( ( commentText = comment - > text ( ) . lower ( ) ) ) . startsWith ( " x-kbibtex-encoding= " ) )
{
m_encoding = commentText . mid ( 19 ) ;
tqDebug ( " Switching encoding to <%s> " , m_encoding . latin1 ( ) ) ;
parameterCommentsList . append ( comment ) ;
}
else
remainingList . append ( * it ) ;
}
}
}
}
int totalElements = ( int ) bibtexfile - > count ( ) ;
int currentPos = 0 ;
const char * encodingTo = m_encoding = = " latex " ? " utf-8 \0 " : m_encoding . append ( " \0 " ) . ascii ( ) ;
m_iconvHandle = iconv_open ( encodingTo , " utf-8 " ) ;
/** before anything else, write parameter comments */
for ( TQValueList < Comment * > : : iterator it = parameterCommentsList . begin ( ) ; it ! = parameterCommentsList . end ( ) & & result & & ! cancelFlag ; it + + )
{
result & = writeComment ( * iodevice , * it ) ;
emit progress ( + + currentPos , totalElements ) ;
}
/** first, write preambles and strings (macros) at the beginning */
for ( TQValueList < Preamble * > : : iterator it = preambleList . begin ( ) ; it ! = preambleList . end ( ) & & result & & ! cancelFlag ; it + + )
{
result & = writePreamble ( * iodevice , * it ) ;
emit progress ( + + currentPos , totalElements ) ;
}
for ( TQValueList < Macro * > : : iterator it = macroList . begin ( ) ; it ! = macroList . end ( ) & & result & & ! cancelFlag ; it + + )
{
result & = writeMacro ( * iodevice , * it ) ;
emit progress ( + + currentPos , totalElements ) ;
}
/** second, write cross-referencing elements */
for ( TQValueList < Entry * > : : iterator it = crossRefingEntryList . begin ( ) ; it ! = crossRefingEntryList . end ( ) & & result & & ! cancelFlag ; it + + )
{
result & = writeEntry ( * iodevice , * it ) ;
emit progress ( + + currentPos , totalElements ) ;
}
/** third, write remaining elements */
for ( TQValueList < Element * > : : iterator it = remainingList . begin ( ) ; it ! = remainingList . end ( ) & & result & & ! cancelFlag ; it + + )
{
Entry * entry = dynamic_cast < Entry * > ( * it ) ;
if ( entry ! = NULL )
result & = writeEntry ( * iodevice , entry ) ;
else
{
Comment * comment = dynamic_cast < Comment * > ( * it ) ;
if ( comment ! = NULL )
result & = writeComment ( * iodevice , comment ) ;
}
emit progress ( + + currentPos , totalElements ) ;
}
iconv_close ( m_iconvHandle ) ;
m_mutex . unlock ( ) ;
return result & & ! cancelFlag ;
}
bool FileExporterBibTeX : : save ( TQIODevice * iodevice , const Element * element , TQStringList * /*errorLog*/ )
{
m_mutex . lock ( ) ;
bool result = FALSE ;
const char * encodingTo = m_encoding = = " latex " ? " utf-8 \0 " : m_encoding . append ( " \0 " ) . ascii ( ) ;
m_iconvHandle = iconv_open ( encodingTo , " utf-8 " ) ;
const Entry * entry = dynamic_cast < const Entry * > ( element ) ;
if ( entry ! = NULL )
result | = writeEntry ( * iodevice , entry ) ;
else
{
const Macro * macro = dynamic_cast < const Macro * > ( element ) ;
if ( macro ! = NULL )
result | = writeMacro ( * iodevice , macro ) ;
else
{
const Comment * comment = dynamic_cast < const Comment * > ( element ) ;
if ( comment ! = NULL )
result | = writeComment ( * iodevice , comment ) ;
else
{
const Preamble * preamble = dynamic_cast < const Preamble * > ( element ) ;
if ( preamble ! = NULL )
result | = writePreamble ( * iodevice , preamble ) ;
}
}
}
iconv_close ( m_iconvHandle ) ;
m_mutex . unlock ( ) ;
return result & & ! cancelFlag ;
}
void FileExporterBibTeX : : cancel ( )
{
cancelFlag = TRUE ;
}
bool FileExporterBibTeX : : writeEntry ( TQIODevice & device , const Entry * entry )
{
writeString ( device , TQString ( " @%1{ %2 " ) . arg ( applyKeywordCasing ( entry - > entryTypeString ( ) ) ) . arg ( entry - > id ( ) ) ) ;
for ( Entry : : EntryFields : : ConstIterator it = entry - > begin ( ) ; it ! = entry - > end ( ) ; + + it )
{
EntryField * field = * it ;
TQString text = valueToString ( field - > value ( ) , field - > fieldType ( ) , field - > fieldTypeName ( ) ) ;
if ( m_protectCasing & & dynamic_cast < BibTeX : : PlainText * > ( field - > value ( ) - > items . first ( ) ) ! = NULL & & ( field - > fieldType ( ) = = EntryField : : ftTitle | | field - > fieldType ( ) = = EntryField : : ftBookTitle | | field - > fieldType ( ) = = EntryField : : ftSeries ) )
addProtectiveCasing ( text ) ;
writeString ( device , TQString ( " , \n \t %1 = %2 " ) . arg ( field - > fieldTypeName ( ) ) . arg ( text ) ) ;
}
writeString ( device , " \n } \n \n " ) ;
return TRUE ;
}
bool FileExporterBibTeX : : writeMacro ( TQIODevice & device , const Macro * macro )
{
TQString text = valueToString ( macro - > value ( ) ) ;
if ( m_protectCasing )
addProtectiveCasing ( text ) ;
writeString ( device , TQString ( " @%1{ %2 = %3 } \n \n " ) . arg ( applyKeywordCasing ( " String " ) ) . arg ( macro - > key ( ) ) . arg ( text ) ) ;
return TRUE ;
}
bool FileExporterBibTeX : : writeComment ( TQIODevice & device , const Comment * comment )
{
if ( ! comment - > useCommand ( ) )
{
TQString text = comment - > text ( ) ;
if ( m_encoding = = " latex " )
text = EncoderLaTeX : : currentEncoderLaTeX ( ) - > encode ( text ) ;
TQStringList commentLines = TQStringList : : split ( ' \n ' , text ) ;
for ( TQStringList : : Iterator it = commentLines . begin ( ) ; it ! = commentLines . end ( ) ; it + + )
{
writeString ( device , ( * it ) . append ( " \n " ) ) ;
}
writeString ( device , " \n " ) ;
}
else
{
TQString text = comment - > text ( ) ;
if ( m_encoding = = " latex " )
text = EncoderLaTeX : : currentEncoderLaTeX ( ) - > encode ( text ) ;
writeString ( device , TQString ( " @%1{%2} \n \n " ) . arg ( applyKeywordCasing ( " Comment " ) ) . arg ( text ) ) ;
}
return TRUE ;
}
bool FileExporterBibTeX : : writePreamble ( TQIODevice & device , const Preamble * preamble )
{
writeString ( device , TQString ( " @%1{%2} \n \n " ) . arg ( applyKeywordCasing ( " Preamble " ) ) . arg ( valueToString ( preamble - > value ( ) ) ) ) ;
return TRUE ;
}
bool FileExporterBibTeX : : writeString ( TQIODevice & device , const TQString & text )
{
size_t utf8datasize = 1 ;
TQCString utf8 = text . utf8 ( ) ;
char * utf8data = utf8 . data ( ) ;
utf8datasize = utf8 . length ( ) ;
char * outputdata = m_iconvBuffer ;
size_t outputdatasize = m_iconvBufferSize ;
size_t result = iconv ( m_iconvHandle , & utf8data , & utf8datasize , & outputdata , & outputdatasize ) ;
if ( result ! = 0 )
{
tqWarning ( " Cannot convert string using iconv " ) ;
return false ;
}
if ( device . writeBlock ( m_iconvBuffer , m_iconvBufferSize - outputdatasize ) ! = ( int ) ( m_iconvBufferSize - outputdatasize ) )
{
tqWarning ( " Cannot write string to device " ) ;
return false ;
}
return true ;
}
void FileExporterBibTeX : : setStringDelimiter ( const TQChar & stringOpenDelimiter , const TQChar & stringCloseDelimiter )
{
m_stringOpenDelimiter = stringOpenDelimiter ;
m_stringCloseDelimiter = stringCloseDelimiter ;
}
void FileExporterBibTeX : : setKeywordCasing ( const KeywordCasing keywordCasing )
{
m_keywordCasing = keywordCasing ;
}
void FileExporterBibTeX : : setEncoding ( const TQString & encoding )
{
m_encoding = encoding ;
}
void FileExporterBibTeX : : setEnclosingCurlyBrackets ( bool protectCasing )
{
m_protectCasing = protectCasing ;
}
TQString FileExporterBibTeX : : valueToString ( const Value * value , const EntryField : : FieldType fieldType , const TQString & fieldTypeName )
{
if ( value = = NULL )
return " " ;
TQString result ;
bool isFirst = TRUE ;
EncoderLaTeX * encoder = EncoderLaTeX : : currentEncoderLaTeX ( ) ;
for ( TQValueList < ValueItem * > : : ConstIterator it = value - > items . begin ( ) ; it ! = value - > items . end ( ) ; + + it )
{
if ( ! isFirst )
result . append ( " # " ) ;
else
isFirst = FALSE ;
MacroKey * macroKey = dynamic_cast < MacroKey * > ( * it ) ;
if ( macroKey ! = NULL )
result . append ( macroKey - > text ( ) ) ;
else
{
TQString text ;
BibTeX : : PersonContainer * personContainer = dynamic_cast < BibTeX : : PersonContainer * > ( * it ) ;
BibTeX : : PlainText * plainText = dynamic_cast < BibTeX : : PlainText * > ( * it ) ;
BibTeX : : KeywordContainer * keywordContainer = dynamic_cast < BibTeX : : KeywordContainer * > ( * it ) ;
if ( plainText ! = NULL )
text = plainText - > text ( ) ;
else if ( keywordContainer ! = NULL )
{
bool first = TRUE ;
for ( TQValueList < Keyword * > : : Iterator it = keywordContainer - > keywords . begin ( ) ; it ! = keywordContainer - > keywords . end ( ) ; + + it )
{
if ( ! first )
text . append ( " , " ) ;
else
first = FALSE ;
text . append ( ( * it ) - > text ( ) ) ;
}
}
else if ( personContainer ! = NULL )
{
bool first = TRUE ;
for ( TQValueList < Person * > : : Iterator it = personContainer - > persons . begin ( ) ; it ! = personContainer - > persons . end ( ) ; + + it )
{
if ( ! first )
text . append ( " and " ) ;
else
first = FALSE ;
TQString v = ( * it ) - > firstName ( ) ;
if ( ! v . isEmpty ( ) )
{
bool requiresQuoting = requiresPersonQuoting ( v , FALSE ) ;
if ( requiresQuoting ) text . append ( " { " ) ;
text . append ( v ) ;
if ( requiresQuoting ) text . append ( " } " ) ;
text . append ( " " ) ;
}
v = ( * it ) - > lastName ( ) ;
if ( ! v . isEmpty ( ) )
{
/** Multi-part surnames (such as "Garcia Marquez") have to be enquoted.
* However , " von " - Parts ( as in " von Hofmannsthal " ) must _not_ be enquoted .
* Examples :
* - - Robson de Souza
* - - Hartmann von der Tann
* - - Ronaldo de { Assis Moreira } ( " Ronaldo de Assis Moreira " works as well )
* - - Ailton { Goncalves da Silva }
* - - Gloria von { Thurn und Taxis }
* Thus we split the von - Parts from the surname ( = everything after the first upcase char ) .
* FIXME : Make the personContainer aware of von - Parts and jr - Parts , instead .
*/
TQStringList list = TQStringList : : split ( " " , v ) ;
TQString von ;
for ( TQStringList : : Iterator it = list . begin ( ) ; it ! = list . end ( ) ; + + it )
{
TQString str = * it ;
if ( str ! = " others " & & str [ 0 ] . category ( ) = = TQChar : : Letter_Lowercase )
{
von + = * it ;
von + = " " ;
}
else
break ;
}
if ( ! von . isEmpty ( ) )
{
text . append ( von ) ;
v = v . right ( v . length ( ) - von . length ( ) ) ;
}
bool requiresQuoting = requiresPersonQuoting ( v , TRUE ) ;
if ( requiresQuoting ) text . append ( " { " ) ;
text . append ( v ) ;
if ( requiresQuoting ) text . append ( " } " ) ;
}
}
}
if ( m_encoding = = " latex " )
text = encoder - > encodeSpecialized ( text , fieldType ) ;
if ( fieldType = = EntryField : : ftURL | | fieldType = = EntryField : : ftDoi | | ( fieldType = = EntryField : : ftUnknown & & fieldTypeName . lower ( ) = = " slaccitation " ) )
removeBackslashQuoting ( text ) ;
/** if the text to save contains a quote char ("),
* force string delimiters to be curly brackets ,
* as quote chars as string delimiters would result
* in parser failures
*/
TQChar stringOpenDelimiter = m_stringOpenDelimiter ;
TQChar stringCloseDelimiter = m_stringCloseDelimiter ;
if ( text . contains ( ' " ' ) & & ( m_stringOpenDelimiter = = ' " ' | | m_stringCloseDelimiter = = ' " ' ) )
{
stringOpenDelimiter = ' { ' ;
stringCloseDelimiter = ' } ' ;
}
result . append ( stringOpenDelimiter ) . append ( text ) . append ( stringCloseDelimiter ) ;
}
}
return result ;
}
void FileExporterBibTeX : : removeBackslashQuoting ( TQString & text )
{
text . replace ( " \\ & " , " & " ) . replace ( " \\ # " , " # " ) . replace ( " \\ _ " , " _ " ) . replace ( " \\ % " , " % " ) ;
}
TQString FileExporterBibTeX : : applyKeywordCasing ( const TQString & keyword )
{
switch ( m_keywordCasing )
{
case kcLowerCase : return keyword . lower ( ) ;
case kcInitialCapital : return keyword . at ( 0 ) + keyword . lower ( ) . mid ( 1 ) ;
case kcCapital : return keyword . upper ( ) ;
default : return keyword ;
}
}
bool FileExporterBibTeX : : requiresPersonQuoting ( const TQString & text , bool isLastName )
{
if ( isLastName & & ! text . contains ( " " ) )
/** Last name contains NO spaces, no quoting necessary */
return FALSE ;
else if ( isLastName & & text [ 0 ] . category ( ) = = TQChar : : Letter_Lowercase )
/** Last name starts with lower case character (e.g. as in "van der Linden") */
return FALSE ;
else if ( ! isLastName & & ! text . contains ( " and " ) )
/** First name contains no " and " no quoting necessary */
return FALSE ;
else if ( text [ 0 ] ! = ' { ' | | text [ text . length ( ) - 1 ] ! = ' } ' )
/** as either last name contains spaces or first name contains " and " and there is no protective quoting yet, there must be a protective quoting added */
return TRUE ;
/** check for cases like "{..}..{..}", which must be surrounded with a protective quoting, too */
int bracketCounter = 0 ;
for ( int i = text . length ( ) - 1 ; i > = 0 ; - - i )
{
if ( text [ i ] = = ' { ' )
+ + bracketCounter ;
else if ( text [ i ] = = ' } ' )
- - bracketCounter ;
if ( bracketCounter = = 0 & & i > 0 )
return TRUE ;
}
return FALSE ;
}
void FileExporterBibTeX : : addProtectiveCasing ( TQString & text )
{
if ( ( text [ 0 ] ! = ' " ' | | text [ text . length ( ) - 1 ] ! = ' " ' ) & & ( text [ 0 ] ! = ' { ' | | text [ text . length ( ) - 1 ] ! = ' } ' ) )
{
/** nothing to protect, as this is no text string */
return ;
}
bool addBrackets = TRUE ;
if ( text [ 1 ] = = ' { ' & & text [ text . length ( ) - 2 ] = = ' } ' )
{
addBrackets = FALSE ;
int count = 0 ;
for ( int i = text . length ( ) - 2 ; ! addBrackets & & i > = 1 ; - - i )
if ( text [ i ] = = ' { ' ) + + count ;
else if ( text [ i ] = = ' } ' ) - - count ;
else if ( count = = 0 ) addBrackets = TRUE ;
}
if ( addBrackets )
text . insert ( 1 , ' { ' ) . insert ( text . length ( ) , ' } ' ) ;
}
}