You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kbibtex/src/entry.cpp

715 lines
25 KiB

/***************************************************************************
* 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 <ntqstring.h>
#include <ntqstringlist.h>
#include <ntqregexp.h>
#include <entry.h>
#include <file.h>
#include <xsltransform.h>
#include <entryfield.h>
#define max(a,b) ((a)>(b)?(a):(b))
namespace BibTeX
{
Entry::Entry( )
: Element(), m_entryType( etUnknown ), m_entryTypeString( TQString::null ), m_id( TQString::null )
{
// nothing
}
Entry::Entry( const EntryType entryType, const TQString &id )
: Element( ), m_entryType( entryType ), m_id( id )
{
m_entryTypeString = entryTypeToString( entryType );
}
Entry::Entry( const TQString& entryTypeString, const TQString& id ) : Element( ), m_entryTypeString( entryTypeString ), m_id( id )
{
m_entryType = entryTypeFromString( entryTypeString );
if ( m_entryType != etUnknown )
m_entryTypeString = entryTypeToString( m_entryType );
}
Entry::Entry( const Entry *other )
{
copyFrom( other );
}
Entry::~Entry()
{
for ( EntryFields::iterator it = m_fields.begin(); it != m_fields.end(); it++ )
{
delete( *it );
}
}
Element* Entry::clone()
{
return new Entry( this );
}
bool Entry::equals( const Entry &other )
{
if ( other.id().compare( id() ) != 0 )
return false;
for ( EntryFields::iterator it = m_fields.begin(); it != m_fields.end(); it++ )
{
EntryField *field1 = *it;
EntryField *field2 = other.getField( field1->fieldTypeName() );
if ( field2 == NULL || field1->value() == NULL || field2->value() == NULL || field1->value()->text().compare( field2->value()->text() ) != 0 )
return false;
}
return true;
}
TQString Entry::text() const
{
TQString result = "Id: ";
result.append( m_id ).append( " (" ).append( entryTypeString() ).append( ")\n" );
for ( EntryFields::ConstIterator it = m_fields.begin(); it != m_fields.end(); it++ )
{
result.append(( *it )->fieldTypeName() ).append( ": " );
result.append(( *it )->value()->text() ).append( "\n" );
}
return result;
}
void Entry::setEntryType( const EntryType entryType )
{
m_entryType = entryType;
m_entryTypeString = entryTypeToString( entryType );
}
void Entry::setEntryTypeString( const TQString& entryTypeString )
{
m_entryTypeString = entryTypeString;
m_entryType = entryTypeFromString( entryTypeString );
}
Entry::EntryType Entry::entryType() const
{
return m_entryType;
}
TQString Entry::entryTypeString() const
{
return m_entryTypeString;
}
void Entry::setId( const TQString& id )
{
m_id = id;
}
TQString Entry::id() const
{
return m_id;
}
bool Entry::containsPattern( const TQString & pattern, EntryField::FieldType fieldType, BibTeX::Element::FilterType filterType, bool caseSensitive ) const
{
if ( filterType == ftExact )
{
/** check for exact match */
bool result = fieldType == EntryField::ftUnknown && m_id.contains( pattern, caseSensitive );
for ( EntryFields::ConstIterator it = m_fields.begin(); !result && it != m_fields.end(); it++ )
if ( fieldType == EntryField::ftUnknown || ( *it ) ->fieldType() == fieldType )
result |= ( *it ) ->value() ->containsPattern( pattern, caseSensitive );
return result;
}
else
{
/** for each word in the search pattern ... */
TQStringList words = TQStringList::split( TQRegExp( "\\s+" ), pattern );
bool *hits = new bool[words.count()];
int i = 0;
for ( TQStringList::Iterator wit = words.begin(); wit != words.end(); ++wit, ++i )
{
hits[i] = fieldType == EntryField::ftUnknown && m_id.contains( *wit, caseSensitive );
/** check if word is contained in any field */
for ( EntryFields::ConstIterator fit = m_fields.begin(); fit != m_fields.end(); ++fit )
if ( fieldType == EntryField::ftUnknown || ( *fit ) ->fieldType() == fieldType )
hits[i] |= ( *fit ) ->value() ->containsPattern( *wit, caseSensitive );
}
unsigned int hitCount = 0;
for ( i = words.count() - 1; i >= 0; --i )
if ( hits[i] ) ++hitCount;
delete[] hits;
/** return success depending on filter type and number of hits */
return (( filterType == ftAnyWord && hitCount > 0 ) || ( filterType == ftEveryWord && hitCount == words.count() ) );
}
}
TQStringList Entry::urls() const
{
TQStringList result;
const TQString fieldNames[] = {"localfile", "pdf", "ps", "postscript", "doi", "url", "howpublished", "ee", "biburl", "note"};
const int fieldNamesCount = sizeof( fieldNames ) / sizeof( fieldNames[0] );
for ( int j = 1; j < 5 ; ++j ) /** there may be variants such as url3 or doi2 */
for ( int i = 0; i < fieldNamesCount; i++ )
{
TQString fieldName = fieldNames[i];
/** field names should be like url, url2, url3, ... */
if ( j > 1 ) fieldName.append( TQString::number( j ) );
EntryField * field = getField( fieldName );
if (( field && !field->value()->items.isEmpty() ) )
{
PlainText *plainText = dynamic_cast<PlainText*>( field->value()->items.first() );
if ( plainText != NULL )
{
TQString plain = plainText->text();
int urlPos = plain.find( "\\url{", 0, FALSE );
if ( urlPos > -1 )
{
plain = plain.mid( urlPos + 5 );
urlPos = plain.find( "}", 0, FALSE );
if ( urlPos > 0 )
plain = plain.left( urlPos - 1 );
}
if ( fieldNames[ i ] == "doi" && !plain.startsWith( "http", FALSE ) )
plain.prepend( "http://dx.doi.org/" );
result.append( plain );
}
}
}
return result;
}
bool Entry::addField( EntryField * field )
{
m_fields.append( field );
return TRUE;
}
EntryField* Entry::getField( const EntryField::FieldType fieldType ) const
{
EntryField * result = NULL;
for ( EntryFields::ConstIterator it = m_fields.begin(); ( it != m_fields.end() ) && ( result == NULL ); it++ )
if (( *it ) ->fieldType() == fieldType ) result = *it;
return result;
}
EntryField* Entry::getField( const TQString & fieldName ) const
{
EntryField * result = NULL;
for ( EntryFields::ConstIterator it = m_fields.begin(); ( it != m_fields.end() ) && ( result == NULL ); it++ )
if (( *it ) ->fieldTypeName().lower() == fieldName.lower() )
result = *it;
return result;
}
bool Entry::deleteField( const TQString & fieldName )
{
for ( EntryFields::ConstIterator it = m_fields.begin(); it != m_fields.end(); it++ )
if (( *it ) ->fieldTypeName().lower() == fieldName.lower() )
{
delete( *it );
m_fields.remove( *it );
return TRUE;
}
return FALSE;
}
bool Entry::deleteField( const EntryField::FieldType fieldType )
{
for ( EntryFields::iterator it = m_fields.begin(); it != m_fields.end(); it++ )
if (( *it ) ->fieldType() == fieldType )
{
delete( *it );
m_fields.remove( it );
return TRUE;
}
return FALSE;
}
Entry::EntryFields::ConstIterator Entry::begin() const
{
return m_fields.constBegin();
}
Entry::EntryFields::ConstIterator Entry::end() const
{
return m_fields.constEnd();
}
int Entry::getFieldCount() const
{
return m_fields.count();
}
void Entry::clearFields()
{
for ( EntryFields::iterator it = m_fields.begin(); it != m_fields.end(); it++ )
delete( *it );
m_fields.clear();
}
void Entry::copyFrom( const Entry *other )
{
if ( other == NULL ) return;
m_entryType = other->m_entryType;
m_entryTypeString = other->m_entryTypeString;
m_id = other->m_id;
clearFields();
for ( EntryFields::ConstIterator it = other->m_fields.begin(); it != other->m_fields.end(); it++ )
m_fields.append( new EntryField( *it ) );
}
void Entry::merge( BibTeX::Entry *other, MergeSemantics mergeSemantics )
{
for ( EntryFields::iterator it = other->m_fields.begin(); it != other->m_fields.end(); it++ )
{
EntryField *otherField = new EntryField( *it );
EntryField::FieldType otherFieldType = otherField->fieldType();
TQString otherFieldTypeName = otherField->fieldTypeName();
EntryField *thisField = otherFieldType != EntryField::ftUnknown ? getField( otherFieldType ) : getField( otherFieldTypeName );
if ( thisField == NULL )
{
m_fields.append( otherField );
}
else if ( otherField->value()->text() == thisField->value()->text() && mergeSemantics == msForceAdding )
{
otherFieldTypeName.prepend( "OPT" );
otherField->setFieldType( EntryField::ftUnknown, otherFieldTypeName );
m_fields.append( otherField );
}
}
}
TQString Entry::entryTypeToString( const EntryType entryType )
{
switch ( entryType )
{
case etArticle:
return TQString( "Article" );
case etBook:
return TQString( "Book" );
case etBooklet:
return TQString( "Booklet" );
case etCollection:
return TQString( "Collection" );
case etElectronic:
return TQString( "Electronic" );
case etInBook:
return TQString( "InBook" );
case etInCollection:
return TQString( "InCollection" );
case etInProceedings:
return TQString( "InProceedings" );
case etManual:
return TQString( "Manual" );
case etMastersThesis:
return TQString( "MastersThesis" );
case etMisc:
return TQString( "Misc" );
case etPhDThesis:
return TQString( "PhDThesis" );
case etProceedings:
return TQString( "Proceedings" );
case etTechReport:
return TQString( "TechReport" );
case etUnpublished:
return TQString( "Unpublished" );
default:
return TQString( "Unknown" );
}
}
Entry::EntryType Entry::entryTypeFromString( const TQString & entryTypeString )
{
TQString entryTypeStringLower = entryTypeString.lower();
if ( entryTypeStringLower == "article" )
return etArticle;
else if ( entryTypeStringLower == "book" )
return etBook;
else if ( entryTypeStringLower == "booklet" )
return etBooklet;
else if ( entryTypeStringLower == "collection" )
return etCollection;
else if (( entryTypeStringLower == "electronic" ) || ( entryTypeStringLower == "online" ) || ( entryTypeStringLower == "internet" ) || ( entryTypeStringLower == "webpage" ) )
return etElectronic;
else if ( entryTypeStringLower == "inbook" )
return etInBook;
else if ( entryTypeStringLower == "incollection" )
return etInCollection;
else if (( entryTypeStringLower == "inproceedings" ) || ( entryTypeStringLower == "conference" ) )
return etInProceedings;
else if ( entryTypeStringLower == "manual" )
return etManual;
else if ( entryTypeStringLower == "mastersthesis" )
return etMastersThesis;
else if ( entryTypeStringLower == "misc" )
return etMisc;
else if ( entryTypeStringLower == "phdthesis" )
return etPhDThesis;
else if ( entryTypeStringLower == "proceedings" )
return etProceedings;
else if ( entryTypeStringLower == "techreport" )
return etTechReport;
else if ( entryTypeStringLower == "unpublished" )
return etUnpublished;
else
return etUnknown;
}
Entry::FieldRequireStatus Entry::getRequireStatus( Entry::EntryType entryType, EntryField::FieldType fieldType )
{
switch ( entryType )
{
case etArticle:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftJournal:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftVolume:
case EntryField::ftMonth:
case EntryField::ftDoi:
case EntryField::ftNumber:
case EntryField::ftPages:
case EntryField::ftNote:
case EntryField::ftKey:
case EntryField::ftISSN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etBook:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftEditor:
case EntryField::ftTitle:
case EntryField::ftPublisher:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftVolume:
case EntryField::ftNumber:
case EntryField::ftSeries:
case EntryField::ftAddress:
case EntryField::ftDoi:
case EntryField::ftEdition:
case EntryField::ftMonth:
case EntryField::ftNote:
case EntryField::ftKey:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etBooklet:
switch ( fieldType )
{
case EntryField::ftTitle:
return Entry::frsRequired;
case EntryField::ftAuthor:
case EntryField::ftHowPublished:
case EntryField::ftAddress:
case EntryField::ftDoi:
case EntryField::ftMonth:
case EntryField::ftYear:
case EntryField::ftNote:
case EntryField::ftKey:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etElectronic:
switch ( fieldType )
{
case EntryField::ftTitle:
case EntryField::ftURL:
return Entry::frsRequired;
case EntryField::ftAuthor:
case EntryField::ftHowPublished:
case EntryField::ftDoi:
case EntryField::ftMonth:
case EntryField::ftYear:
case EntryField::ftKey:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etInBook:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftEditor:
case EntryField::ftTitle:
case EntryField::ftPages:
case EntryField::ftChapter:
case EntryField::ftPublisher:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftVolume:
case EntryField::ftSeries:
case EntryField::ftAddress:
case EntryField::ftDoi:
case EntryField::ftEdition:
case EntryField::ftMonth:
case EntryField::ftNote:
case EntryField::ftCrossRef:
case EntryField::ftKey:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etInCollection:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftBookTitle:
case EntryField::ftPublisher:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftEditor:
case EntryField::ftPages:
case EntryField::ftOrganization:
case EntryField::ftAddress:
case EntryField::ftMonth:
case EntryField::ftLocation:
case EntryField::ftNote:
case EntryField::ftCrossRef:
case EntryField::ftDoi:
case EntryField::ftKey:
case EntryField::ftType:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etInProceedings:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftYear:
case EntryField::ftBookTitle:
return Entry::frsRequired;
case EntryField::ftPages:
case EntryField::ftEditor:
case EntryField::ftVolume:
case EntryField::ftNumber:
case EntryField::ftSeries:
case EntryField::ftType:
case EntryField::ftChapter:
case EntryField::ftAddress:
case EntryField::ftDoi:
case EntryField::ftEdition:
case EntryField::ftLocation:
case EntryField::ftMonth:
case EntryField::ftNote:
case EntryField::ftCrossRef:
case EntryField::ftPublisher:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etManual:
switch ( fieldType )
{
case EntryField::ftTitle:
return Entry::frsRequired;
case EntryField::ftAuthor:
case EntryField::ftOrganization:
case EntryField::ftAddress:
case EntryField::ftDoi:
case EntryField::ftEdition:
case EntryField::ftMonth:
case EntryField::ftYear:
case EntryField::ftNote:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etMastersThesis:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftSchool:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftAddress:
case EntryField::ftMonth:
case EntryField::ftDoi:
case EntryField::ftNote:
case EntryField::ftKey:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etMisc:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftHowPublished:
case EntryField::ftMonth:
case EntryField::ftYear:
case EntryField::ftDoi:
case EntryField::ftNote:
case EntryField::ftKey:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etPhDThesis:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftSchool:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftAddress:
case EntryField::ftMonth:
case EntryField::ftNote:
case EntryField::ftDoi:
case EntryField::ftKey:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etCollection:
case etProceedings:
switch ( fieldType )
{
case EntryField::ftTitle:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftEditor:
case EntryField::ftPublisher:
case EntryField::ftOrganization:
case EntryField::ftAddress:
case EntryField::ftMonth:
case EntryField::ftLocation:
case EntryField::ftNote:
case EntryField::ftDoi:
case EntryField::ftKey:
case EntryField::ftSeries:
case EntryField::ftBookTitle:
case EntryField::ftISBN:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etTechReport:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftInstitution:
case EntryField::ftYear:
return Entry::frsRequired;
case EntryField::ftType:
case EntryField::ftDoi:
case EntryField::ftNumber:
case EntryField::ftAddress:
case EntryField::ftMonth:
case EntryField::ftNote:
case EntryField::ftKey:
case EntryField::ftURL:
case EntryField::ftLocalFile:
case EntryField::ftISSN:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
case etUnpublished:
switch ( fieldType )
{
case EntryField::ftAuthor:
case EntryField::ftTitle:
case EntryField::ftNote:
return Entry::frsRequired;
case EntryField::ftMonth:
case EntryField::ftYear:
case EntryField::ftDoi:
case EntryField::ftKey:
case EntryField::ftURL:
case EntryField::ftLocalFile:
return Entry::frsOptional;
default:
return Entry::frsIgnored;
}
default:
return Entry::frsOptional;
}
}
}