/*************************************************************************** * 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 #include #include #include #include #include #include "idsuggestions.h" namespace KBibTeX { const TQRegExp IdSuggestions::unwantedChars = TQRegExp( "[^-_:/=+a-zA-Z0-9]+" ); IdSuggestions::IdSuggestions() { // nothing } IdSuggestions::~IdSuggestions() { // nothing } /** * Determine list of authors or editors for a given entry */ TQStringList IdSuggestions::authorsLastName( BibTeX::Entry *entry ) { TQStringList result; /** retrieve field holding authors information for entry */ BibTeX::EntryField *field = entry->getField( BibTeX::EntryField::ftAuthor ); if ( field == NULL ) { /** no author field available, try editor patch by Jurgen Spitzmuller */ field = entry->getField( BibTeX::EntryField::ftEditor ); if ( field == NULL ) { return result; /** neither author nor editor available */ } } /** fetch container holding list of author names */ BibTeX::PersonContainer *personContainer = field != NULL ? dynamic_cast( field->value()->items.isEmpty() ? NULL : field->value()->items.first() ) : NULL; if ( personContainer == NULL || personContainer->persons.isEmpty() ) return result; /** container not found or is empty */ /** iterate through container and fetch each author's last name */ for ( TQValueList::ConstIterator it = personContainer->persons.begin(); it != personContainer->persons.end(); ++it ) { result.append( normalizeText(( *it )->lastName() ) ); } return result; } /** * Normalize a given text by removing spaces or other unwanted characters, * replace some accented and special characters by plain ASCII transliterations */ TQString IdSuggestions::normalizeText( const TQString& text ) { TQString result = text; for ( int i = text.length() - 1; i >= 0; --i ) result[i] = TQChar( BibTeX::EncoderLaTeX::unicodeToASCII( result[i].unicode() ) ); return result.replace( unwantedChars, "" ); } TQString IdSuggestions::resolveConflict( BibTeX::File *file, const TQString &id, BibTeX::Element *element ) { TQString result = id; BibTeX::Element *hit = file->containsKey( id ) ; if ( hit != NULL && hit != element ) { int i = 0; do { result = TQString( "%1-%2" ).arg( id ).arg( ++i ); hit = file->containsKey( result ); } while ( hit != NULL && hit != element ); } return result; } /** * Determine year for a given entry */ int IdSuggestions::extractYear( BibTeX::Entry *entry ) { /** retrieve field holding year information for entry */ BibTeX::EntryField *field = entry->getField( BibTeX::EntryField::ftYear ); if ( field == NULL ) return -1; /** no year field available */ /** fetch value item holding year */ BibTeX::ValueItem *valueItem = field != NULL ? ( field->value()->items.isEmpty() ? NULL : field->value()->items.first() ) : NULL; if ( valueItem == NULL ) return -1; /** no value item found or is empty */ /** parse value item's text */ bool ok = FALSE; TQRegExp yearRegExp( "\\b(\\d{2})?\\d{2}\\b" ); yearRegExp.search( valueItem->text() ); int year = TQString( yearRegExp.cap( 0 ) ).toInt( &ok ); if ( !ok ) year = -1; return year; } /** * Determine title for a given entry */ TQString IdSuggestions::extractTitle( BibTeX::Entry *entry ) { /** retrieve field holding title information for entry */ BibTeX::EntryField *field = entry->getField( BibTeX::EntryField::ftTitle ); if ( field == NULL ) return TQString::null; /** no title field available */ /** *fetch value item holding title */ BibTeX::ValueItem *valueItem = field->value()->items.isEmpty() ? NULL : field->value()->items.first(); if ( valueItem == NULL ) return TQString::null; /** no value item found or is empty */ return valueItem->text(); } TQStringList IdSuggestions::createSuggestions( BibTeX::File *file, BibTeX::Entry *entry ) { Settings * settings = Settings::self(); const TQStringList& formatStrList = settings->idSuggestions_formatStrList; TQStringList result; TQStringList allKeys = file != NULL ? file->allKeys() : TQStringList(); entry = new BibTeX::Entry( entry ); if ( file != NULL ) file->completeReferencedFields( entry ); for ( TQStringList::ConstIterator it = formatStrList.begin(); it != formatStrList.end(); ++it ) { TQString id = formatId( entry, *it ); if ( id.isEmpty() || result.contains( id ) ) continue; if ( !result.contains( id ) ) result.append( id ); } delete entry; return result; } TQString IdSuggestions::createDefaultSuggestion( BibTeX::File *file, BibTeX::Entry *entry ) { Settings * settings = Settings::self(); if ( settings->idSuggestions_default < 0 || settings->idSuggestions_default >= ( int )settings->idSuggestions_formatStrList.size() ) return TQString::null; entry = new BibTeX::Entry( entry ); if ( file != NULL ) file->completeReferencedFields( entry ); TQString result = formatId( entry, settings->idSuggestions_formatStrList[settings->idSuggestions_default] ); delete entry; return result; } TQString IdSuggestions::formatId( BibTeX::Entry *entry, const TQString& formatStr ) { TQString id; TQStringList tokenList = TQStringList::split( '|', formatStr ); for ( TQStringList::ConstIterator tit = tokenList.begin(); tit != tokenList.end(); ++tit ) id.append( translateToken( entry, *tit ) ); return id; } TQString IdSuggestions::translateToken( BibTeX::Entry *entry, const TQString& token ) { switch ( token[0] ) { case 'a': return translateAuthorsToken( entry, token.mid( 1 ), aOnlyFirst ); case 'A': return translateAuthorsToken( entry, token.mid( 1 ), aAll ); case 'z': return translateAuthorsToken( entry, token.mid( 1 ), aNotFirst ); case 'y': { int year = extractYear( entry ); if ( year > -1 ) return TQString::number( year % 100 + 100 ).mid( 1 ); else return TQString::null; } case 'Y': { int year = extractYear( entry ); if ( year > -1 ) return TQString::number( year % 10000 + 10000 ).mid( 1 ); else return TQString::null; } case 't': return translateTitleToken( entry, token.mid( 1 ), FALSE ); case 'T': return translateTitleToken( entry, token.mid( 1 ), TRUE ); case '"': return token.mid( 1 ); default: return TQString::null; } } TQString IdSuggestions::translateAuthorsToken( BibTeX::Entry *entry, const TQString& token, Authors selectAuthors ) { struct IdSuggestionTokenInfo ati = evalToken( token ); TQString result; bool first = true, firstInserted = true; TQStringList authors = authorsLastName( entry ); for ( TQStringList::ConstIterator it = authors.begin(); it != authors.end(); ++it ) { TQString author = normalizeText( *it ).left( ati.len ); if ( selectAuthors == aAll || ( selectAuthors == aOnlyFirst && first ) || ( selectAuthors == aNotFirst && !first ) ) { if ( !firstInserted ) result.append( ati.inBetween ); result.append( author ); firstInserted = false; } first = false; } if ( ati.toUpper ) result = result.upper(); else if ( ati.toLower ) result = result.lower(); return result; } struct IdSuggestionTokenInfo IdSuggestions::evalToken( const TQString& token ) { unsigned int pos = 0; struct IdSuggestionTokenInfo result; result.len = 0x00ffffff; result.toLower = FALSE; result.toUpper = FALSE; result.inBetween = TQString::null; if ( token.length() > pos ) { int dv = token[pos].digitValue(); if ( dv > -1 ) { result.len = dv; ++pos; } } if ( token.length() > pos ) { result.toLower = token[pos] == 'l'; result.toUpper = token[pos] == 'u'; if ( result.toUpper || result.toLower ) ++pos; } if ( token.length() > pos + 1 && token[pos] == '"' ) result.inBetween = token.mid( pos + 1 ); return result; } TQString IdSuggestions::translateTitleToken( BibTeX::Entry *entry, const TQString& token, bool removeSmallWords ) { struct IdSuggestionTokenInfo tti = evalToken( token ); Settings * settings = Settings::self(); const TQStringList smallWords = settings->idSuggestions_smallWords; TQString result; bool first = TRUE; TQStringList titleWords = TQStringList::split( TQRegExp( "\\s+" ), extractTitle( entry ) ); for ( TQStringList::ConstIterator it = titleWords.begin(); it != titleWords.end(); ++it ) { if ( first ) first = FALSE; else result.append( tti.inBetween ); TQString lowerText = ( *it ).lower(); if ( !removeSmallWords || !smallWords.contains( lowerText ) ) result.append( normalizeText( *it ).left( tti.len ) ); } if ( tti.toUpper ) result = result.upper(); else if ( tti.toLower ) result = result.lower(); return result; } /** convert a formatting string into a human readable version (even translated) */ TQString IdSuggestions::formatStrToHuman( const TQString& formatStr ) { bool first = TRUE; TQString text; TQStringList elements = TQStringList::split( '|', formatStr ); for ( TQStringList::iterator it = elements.begin();it != elements.end();++it ) { if ( first ) first = FALSE; else text.append( "\n" ); if (( *it )[0] == 'a' || ( *it )[0] == 'A' || ( *it )[0] == 'z' ) { struct IdSuggestionTokenInfo info = evalToken(( *it ).mid( 1 ) ); if (( *it )[0] == 'a' ) text.append( i18n( "First author only" ) ); else if (( *it )[0] == 'z' ) text.append( i18n( "All but first author" ) ); else text.append( i18n( "All authors" ) ); int n = info.len; if ( info.len < 0x00ffffff ) text.append( i18n( ", but only first letter of each last name", ", but only first %n letters of each last name", n ) ); if ( info.toUpper ) text.append( i18n( ", in upper case" ) ); else if ( info.toLower ) text.append( i18n( ", in lower case" ) ); if ( info.inBetween != TQString::null ) text.append( TQString( i18n( ", with '%1' in between" ) ).arg( info.inBetween ) ); } else if (( *it )[0] == 'y' ) text.append( i18n( "Year (2 digits)" ) ); else if (( *it )[0] == 'Y' ) text.append( i18n( "Year (4 digits)" ) ); else if (( *it )[0] == 't' || ( *it )[0] == 'T' ) { struct IdSuggestionTokenInfo info = evalToken(( *it ).mid( 1 ) ); text.append( i18n( "Title" ) ); int n = info.len; if ( info.len < 0x00ffffff ) text.append( i18n( ", but only first letter of each word", ", but only first %n letters of each word", n ) ); if ( info.toUpper ) text.append( i18n( ", in upper case" ) ); else if ( info.toLower ) text.append( i18n( ", in lower case" ) ); if ( info.inBetween != TQString::null ) text.append( TQString( i18n( ", with '%1' in between" ) ).arg( info.inBetween ) ); if (( *it )[0] == 'T' ) text.append( i18n( ", small words removed" ) ); } else if (( *it )[0] == '"' ) text.append( TQString( i18n( "Text: '%1'" ) ).arg(( *it ).mid( 1 ) ) ); else text.append( "?" ); } return text; } }