summaryrefslogtreecommitdiffstats
path: root/src/importers/mxpimporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/importers/mxpimporter.cpp')
-rw-r--r--src/importers/mxpimporter.cpp382
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();
+}