diff options
Diffstat (limited to 'src/importers/mxpimporter.cpp')
-rw-r--r-- | src/importers/mxpimporter.cpp | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/src/importers/mxpimporter.cpp b/src/importers/mxpimporter.cpp new file mode 100644 index 0000000..20fbf0f --- /dev/null +++ b/src/importers/mxpimporter.cpp @@ -0,0 +1,382 @@ +/*************************************************************************** +* Copyright (C) 2003 by * +* Jason Kivlighn ([email protected]) * +* * +* 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. * +***************************************************************************/ + +#include "mxpimporter.h" + +#include <tqfile.h> +#include <tqtextstream.h> +#include <tqstringlist.h> +#include <tqdatetime.h> + +#include <tdeapplication.h> +#include <tdelocale.h> +#include <kdebug.h> + +#include "datablocks/mixednumber.h" +#include "datablocks/recipe.h" + +MXPImporter::MXPImporter() : BaseImporter() +{} + +void MXPImporter::parseFile( const TQString &file ) +{ + TQFile input( file ); + + if ( input.open( IO_ReadOnly ) ) { + TQTextStream stream( &input ); + stream.skipWhiteSpace(); + + TQString line; + while ( !stream.atEnd() ) { + line = stream.readLine().stripWhiteSpace(); + + if ( line.simplifyWhiteSpace().contains( "Exported from MasterCook" ) ) { + importMXP( stream ); + } + else if ( line == "{ Exported from MasterCook Mac }" ) { + importMac( stream ); + } + else if ( line == "@@@@@" ) { + importGeneric( stream ); + } + + stream.skipWhiteSpace(); + } + + if ( fileRecipeCount() == 0 ) + addWarningMsg( i18n( "No recipes found in this file." ) ); + } + else + setErrorMsg( i18n( "Unable to open file." ) ); +} + +MXPImporter::~MXPImporter() +{} + +void MXPImporter::importMXP( TQTextStream &stream ) +{ + Recipe recipe; + + kapp->processEvents(); //don't want the user to think its frozen... especially for files with thousands of recipes + + //kdDebug()<<"Found recipe MXP format: * Exported from MasterCook *"<<endl; + TQString current; + + // title + stream.skipWhiteSpace(); + recipe.title = stream.readLine().stripWhiteSpace(); + //kdDebug()<<"Found title: "<<m_title<<endl; + + //author + stream.skipWhiteSpace(); + current = stream.readLine().stripWhiteSpace(); + if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "recipe by" ) { + Element new_author( current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace() ); + recipe.authorList.append( new_author ); + //kdDebug()<<"Found author: "<<new_author.name<<endl; + } + else { + addWarningMsg( TQString( i18n( "While loading recipe \"%1\" " + "the field \"Recipe By:\" is either missing or could not be detected." ) ).arg( recipe.title ) ); + } + + //servings + stream.skipWhiteSpace(); + current = stream.readLine().stripWhiteSpace(); + if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "serving size" ) { + //allows serving size to be loaded even if preparation time is missing + int end_index; + if ( current.contains( "preparation time", FALSE ) ) + end_index = current.find( "preparation time", 0, FALSE ) - 15; + else + end_index = current.length(); + + recipe.yield.amount = current.mid( current.find( ":" ) + 1, end_index ).stripWhiteSpace().toInt(); + recipe.yield.type = i18n("servings"); + //kdDebug()<<"Found serving size: "<<recipe.yield.amount<<endl; + } + else { + addWarningMsg( TQString( i18n( "While loading recipe \"%1\" " + "the field \"Serving Size:\" is either missing or could not be detected." ) ).arg( recipe.title ) ); + } + + if ( current.contains( "preparation time", FALSE ) ) { + TQString prep_time = current.mid( current.find( ":", current.find( "preparation time", 0, FALSE ) ) + 1, + current.length() ).stripWhiteSpace(); + recipe.prepTime = TQTime( prep_time.section( ':', 0, 0 ).toInt(), prep_time.section( ':', 1, 1 ).toInt() ); + kdDebug() << "Found preparation time: " << prep_time << endl; + } + else { + addWarningMsg( TQString( i18n( "While loading recipe \"%1\" " + "the field \"Preparation Time:\" is either missing or could not be detected." ) ).arg( recipe.title ) ); + } + + loadCategories( stream, recipe ); + loadIngredients( stream, recipe ); + loadInstructions( stream, recipe ); + loadOptionalFields( stream, recipe ); + + add + ( recipe ); + + if ( !stream.atEnd() ) { + importMXP( stream ); + return ; + } +} + +void MXPImporter::loadCategories( TQTextStream &stream, Recipe &recipe ) +{ + //====================categories====================// + stream.skipWhiteSpace(); + TQString current = stream.readLine().stripWhiteSpace(); + if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "categories" ) { + TQString tmp_str = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + + while ( current.stripWhiteSpace() != "Amount Measure Ingredient -- Preparation Method" && !stream.atEnd() ) { + if ( !tmp_str.isEmpty() ) { + TQStringList categories = TQStringList::split( " ", tmp_str ); + for ( TQStringList::const_iterator it = categories.begin(); it != categories.end(); ++it ) { + Element new_cat( ( *it ).stripWhiteSpace() ); + recipe.categoryList.append( new_cat ); + + //kdDebug()<<"Found category: "<<new_cat.name<<endl; + } + } + + current = stream.readLine(); + tmp_str = current; + } + //else + // kdDebug()<<"No categories found."<<endl; + } + else { + addWarningMsg( TQString( i18n( "While loading recipe \"%1\" " + "the field \"Categories:\" is either missing or could not be detected." ) ).arg( recipe.title ) ); + + //the ingredient loaded will expect the last thing to have been read to be this header line + while ( current.stripWhiteSpace() != "Amount Measure Ingredient -- Preparation Method" && !stream.atEnd() ) + current = stream.readLine(); + } +} + +void MXPImporter::loadIngredients( TQTextStream &stream, Recipe &recipe ) +{ + //============ingredients=================// + stream.skipWhiteSpace(); + ( void ) stream.readLine(); + TQString current = stream.readLine(); + if ( !current.contains( "NONE" ) && !current.isEmpty() ) { + while ( !current.isEmpty() && !stream.atEnd() ) { + Ingredient new_ingredient; + + //amount + TQString amount_str = current.mid( 0, 9 ).simplifyWhiteSpace(); + if ( !amount_str.isEmpty() ) // case of amount_str.isEmpty() correctly handled by class default + { + bool ok; + MixedNumber amount( MixedNumber::fromString( amount_str, &ok ) ); + if ( !ok ) + { + addWarningMsg( TQString( i18n( "While loading recipe \"%1\" Invalid amount \"%2\" in the line \"%3\"" ) ).arg( recipe.title ).arg( amount_str ).arg( current.stripWhiteSpace() ) ); + current = stream.readLine(); + continue; + } + new_ingredient.amount = amount.toDouble(); + } + + //units + TQString units( current.mid( 9, 13 ) ); + new_ingredient.units = Unit( units.simplifyWhiteSpace(), new_ingredient.amount ); + + //name + int dash_index = current.find( "--" ); + + int length; + if ( dash_index == -1 || dash_index == 24 ) //ignore a dash in the first position (index 24) + length = current.length(); + else + length = dash_index - 22; + + TQString ingredient_name( current.mid( 22, length ) ); + new_ingredient.name = ingredient_name.stripWhiteSpace(); + + //prep method + if ( dash_index != -1 && dash_index != 24 ) //ignore a dash in the first position (index 24) + new_ingredient.prepMethodList.append( Element(current.mid( dash_index + 2, current.length() ).stripWhiteSpace()) ); + + recipe.ingList.append( new_ingredient ); + //kdDebug()<<"Found ingredient: amount="<<new_ingredient.amount + // <<", unit:"<<new_ingredient.units + // <<", name:"<<new_ingredient.name + // <<", prep_method:"<<prep_method<<endl; + + current = stream.readLine(); + } + } + //else + // kdDebug()<<"No ingredients found."<<endl; +} + +void MXPImporter::loadInstructions( TQTextStream &stream, Recipe &recipe ) +{ + //==========================instructions ( along with other optional fields... mxp format doesn't define end of ingredients and start of other fields )==============// + stream.skipWhiteSpace(); + TQString current = stream.readLine().stripWhiteSpace(); + while ( !current.contains( "- - - -" ) && !stream.atEnd() ) { + if ( current.stripWhiteSpace() == "Source:" ) { + Element new_author( getNextQuotedString( stream ) ); + recipe.authorList.append( new_author ); + //kdDebug()<<"Found source: "<<new_author.name<<" (adding as author)"<<endl; + } + else if ( current.stripWhiteSpace() == "Description:" ) { + TQString description = getNextQuotedString( stream ); + //kdDebug()<<"Found description: "<<m_description<<" (adding to end of instructions)"<<endl; + recipe.instructions += "\n\nDescription: " + description; + } + else if ( current.stripWhiteSpace() == "S(Internet Address):" ) { + TQString internet = getNextQuotedString( stream ); + //kdDebug()<<"Found internet address: "<<m_internet<<" (adding to end of instructions)"<<endl; + recipe.instructions += "\n\nInternet address: " + internet; + } + else if ( current.stripWhiteSpace() == "Yield:" ) { + recipe.yield.amount = getNextQuotedString( stream ).stripWhiteSpace().toInt(); + recipe.yield.type = i18n("servings"); + //kdDebug()<<"Found yield: "<<recipe.yield.amount<<" (adding as servings)"<<endl; + } + else if ( current.stripWhiteSpace() == "T(Cook Time):" ) { + ( void ) getNextQuotedString( stream ); //this would be prep time, but we don't use prep time at the moment + //kdDebug()<<"Found cook time: "<<m_prep_time<<" (adding as prep time)"<<endl; + } + else if ( current.stripWhiteSpace() == "Cuisine:" ) { + Element new_cat( getNextQuotedString( stream ) ); + recipe.categoryList.append( new_cat ); + //kdDebug()<<"Found cuisine (adding as category): "<<new_cat.name<<endl; + } + else + recipe.instructions += current + "\n"; + + current = stream.readLine().stripWhiteSpace(); + } + recipe.instructions = recipe.instructions.stripWhiteSpace(); + //kdDebug()<<"Found instructions: "<<m_instructions<<endl; +} + +void MXPImporter::loadOptionalFields( TQTextStream &stream, Recipe &recipe ) +{ + //=================after here, fields are optional=========================// + stream.skipWhiteSpace(); + TQString current = stream.readLine().stripWhiteSpace(); + + TQString notes; + + //Note: we simplifyWhiteSpace() because some versions of MasterCook have "Exported from MasterCook" and others have "Exported from MasterCook". + // This also could work around a typo or such. + while ( !current.simplifyWhiteSpace().contains( "Exported from MasterCook" ) && !stream.atEnd() ) { + //suggested wine + if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "suggested wine" ) { + TQString wine = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + //kdDebug()<<"Found suggested wine: "<<m_wine<<" (adding to end of instructions)"<<endl; + + recipe.instructions += "\n\nSuggested wine: " + wine; + } + //Nutr. Assoc. + if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "nutr. assoc." ) { + TQString nutr_assoc = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + //kdDebug()<<"Found nutrient association: "<<nutr_assoc<<" (adding to end of instructions)"<<endl; + + recipe.instructions += "\n\nNutrient Association: " + nutr_assoc; + } + else if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "per serving (excluding unknown items)" ) { //per serving... maybe we can do something with this info later + TQString per_serving_info = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + //kdDebug()<<"Found per serving (excluding unknown items): "<<per_serving_info<<" (adding to end of instructions)"<<endl; + + recipe.instructions += "\n\nPer Serving (excluding unknown items): " + per_serving_info; + } + else if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "per serving" ) { //per serving... maybe we can do something with this info later + TQString per_serving_info = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + //kdDebug()<<"Found per serving: "<<per_serving_info<<" (adding to end of instructions)"<<endl; + + recipe.instructions += "\n\nPer Serving: " + per_serving_info; + } + else if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "food exchanges" ) { //food exchanges... maybe we can do something with this info later + TQString food_exchange_info = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + //kdDebug()<<"Found food exchanges: "<<food_exchange_info<<" (adding to end of instructions)"<<endl; + + recipe.instructions += "\n\nFood Exchanges: " + food_exchange_info; + } + else if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "serving ideas" ) { //serving ideas + TQString serving_ideas = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + //kdDebug()<<"Found serving ideas: "<<m_serving_ideas<<" (adding to end of instructions)"<<endl; + + recipe.instructions += "\n\nServing ideas: " + serving_ideas; + } + else if ( current.mid( 0, current.find( ":" ) ).simplifyWhiteSpace().lower() == "notes" ) //notes + notes = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); + else if ( !current.isEmpty() && current != "_____" ) //if it doesn't belong to any other field, assume it a part of a multi-line notes field + notes += "\n" + current; + + current = stream.readLine().stripWhiteSpace(); + } + + /*possible fields to implement later: + + Nutr. Assoc. : 0 0 0 0 0 + + Ratings : Cholesterol Rating 5 Complete Meal 3 + Cost 3 Depth 3 + Difficulty 2 Fanciness 7 + Fat Content 5 Good For Crowds 10 + Intensity 5 Intricacy 2 + Kid Appeal 3 Looks 5 + Portability 3 Richness 7 + Serving Temperature 8 Spicy Hotness 2 + Tartness 7 + + */ + if ( !notes.isNull() ) { + //kdDebug()<<TQString("Found notes: %s (adding to end of instructions)").arg(m_notes)<<endl; + recipe.instructions += "\n\nNotes: " + notes.stripWhiteSpace(); + } +} + +void MXPImporter::importGeneric( TQTextStream & /*stream*/ ) +{ + setErrorMsg( i18n( "MasterCook's Generic Export format is currently not supported. Please write to [email protected] to request support for this format." ) ); + //not even sure it this is worth writing... its rather obsolete +} + +void MXPImporter::importMac( TQTextStream & /*stream*/ ) +{ + setErrorMsg( i18n( "MasterCook Mac's Export format is currently not supported. Please write to [email protected] to request support for this format." ) ); + //not even sure it this is worth writing... its rather obsolete +} + +TQString MXPImporter::getNextQuotedString( TQTextStream &stream ) +{ + stream.skipWhiteSpace(); + TQString current = stream.readLine().stripWhiteSpace(); + TQString return_str; + + if ( current.left( 1 ) == "\"" ) + return_str = current.mid( 1, current.length() - 1 ); + else + return current; + + while ( current.right( 1 ) != "\"" && !stream.atEnd() ) { + current = stream.readLine().stripWhiteSpace(); + return_str += "\n" + current; + } + + //take off quote at end + return_str = return_str.mid( 0, return_str.length() - 1 ); + + return return_str.stripWhiteSpace(); +} |