/*************************************************************************** * Copyright (C) 2003 by * * Jason Kivlighn (jkivlighn@gmail.com) * * * * 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 "mmfimporter.h" #include #include #include #include #include #include #include #include "datablocks/mixednumber.h" #include "datablocks/recipe.h" #include "mmdata.h" //TODO: pre-parse file and try to correct alignment errors in ingredients? MMFImporter::MMFImporter() : BaseImporter() {} MMFImporter::~MMFImporter() {} void MMFImporter::parseFile( const TQString &file ) { resetVars(); TQFile input( file ); if ( input.open( IO_ReadOnly ) ) { TQTextStream stream( &input ); stream.skipWhiteSpace(); TQString line; while ( !stream.atEnd() ) { line = stream.readLine(); if ( line.startsWith( "MMMMM" ) ) { version = VersionMMMMM; importMMF( stream ); } else if ( line.contains( "Recipe Extracted from Meal-Master (tm) Database" ) ) { version = FromDatabase; importMMF( stream ); } else if ( line.startsWith( "-----" ) ) { version = VersionNormal; importMMF( stream ); } else if ( line.startsWith( "MM" ) ) { version = VersionBB; ( void ) stream.readLine(); importMMF( stream ); } stream.skipWhiteSpace(); } if ( fileRecipeCount() == 0 ) addWarningMsg( i18n( "No recipes found in this file." ) ); } else setErrorMsg( i18n( "Unable to open file." ) ); } void MMFImporter::importMMF( TQTextStream &stream ) { kapp->processEvents(); //don't want the user to think its frozen... especially for files with thousands of recipes TQString current; //===============FIXED FORMAT================// //line 1: title //line 2: categories (comma or space separated) //line 3: yield (number followed by label) //title stream.skipWhiteSpace(); current = stream.readLine(); m_title = current.mid( current.find( ":" ) + 1, current.length() ).stripWhiteSpace(); kdDebug() << "Found title: " << m_title << endl; //categories stream.skipWhiteSpace(); current = stream.readLine().stripWhiteSpace(); const char separator = ( version == FromDatabase ) ? ' ' : ','; TQStringList categories = TQStringList::split( separator, current.mid( current.find( ":" ) + 1, current.length() ) ); for ( TQStringList::const_iterator it = categories.begin(); it != categories.end(); ++it ) { Element new_cat; new_cat.name = TQString( *it ).stripWhiteSpace(); kdDebug() << "Found category: " << new_cat.name << endl; m_categories.append( new_cat ); } //servings stream.skipWhiteSpace(); current = stream.readLine().stripWhiteSpace(); if ( current.startsWith( "Yield:" ) ) { //get the number between the ":" and the next space after it m_servings = current.mid( current.find( ":" ) + 1, current.find( " ", current.find( ":" ) + 2 ) - current.find( ":" ) ).toInt(); kdDebug() << "Found yield: " << m_servings << endl; } else if ( current.startsWith( "Servings:" ) ) //from database version { m_servings = current.mid( current.find( ":" ) + 1, current.length() ).toInt(); kdDebug() << "Found servings: " << m_servings << endl; } //=======================VARIABLE FORMAT===================// //read lines until ending is found //each line is either an ingredient, ingredient header, or instruction bool instruction_found = false; bool is_sub = false; ( void ) stream.readLine(); current = stream.readLine(); while ( current.stripWhiteSpace() != "MMMMM" && current.stripWhiteSpace() != "-----" && current.stripWhiteSpace() != "-----------------------------------------------------------------------------" && !stream.atEnd() ) { bool col_one_used = loadIngredientLine( current.left( 41 ), m_left_col_ing, is_sub ); if ( col_one_used ) //only check for second column if there is an ingredient in the first column loadIngredientLine( current.mid( 41, current.length() ), m_right_col_ing, is_sub ); if ( instruction_found && col_one_used ) { addWarningMsg( TQString( i18n( "While loading recipe %1 " "an ingredient line was found after the directions. " "While this is valid, it most commonly indicates an incorrectly " "formatted recipe." ) ).arg( m_title ) ); } if ( !col_one_used && !loadIngredientHeader( current.stripWhiteSpace() ) ) { if ( !current.stripWhiteSpace().isEmpty() ) instruction_found = true; m_instructions += current.stripWhiteSpace() + "\n"; //kdDebug()<<"Found instruction line: "< 1 ) new_ingredient.units.plural = unit; else new_ingredient.units.name = unit; } //unit/name separator if ( string[ 10 ] != ' ' || string[ 11 ] == ' ' ) return false; //name and preparation method new_ingredient.name = string.mid( 11, 41 ).stripWhiteSpace(); //put in the header... it there is no header, current_header will be TQString::null new_ingredient.group = current_header; bool last_is_sub = is_sub; if ( new_ingredient.name.endsWith(", or") ) { new_ingredient.name = new_ingredient.name.left(new_ingredient.name.length()-4); is_sub = true; } else is_sub = false; if ( last_is_sub ) ( *list.at( list.count() - 1 ) ).substitutes.append(new_ingredient); else list.append( new_ingredient ); //if we made it this far it is an ingredient line return true; } bool MMFImporter::loadIngredientHeader( const TQString &string ) { if ( ( string.startsWith( "-----" ) || string.startsWith( "MMMMM" ) ) && string.length() >= 40 && ( ( string.at( string.length() / 2 ) != "-" ) || ( string.at( string.length() / 2 + 1 ) != "-" ) || ( string.at( string.length() / 2 - 1 ) != "-" ) ) ) { TQString header( string.stripWhiteSpace() ); //get only the header name header.remove( TQRegExp( "^MMMMM" ) ); header.remove( TQRegExp( "^-*" ) ).remove( TQRegExp( "-*$" ) ); kdDebug() << "found ingredient header: " << header << endl; //merge all columns before appending to full ingredient list to maintain the ingredient order for ( IngredientList::iterator ing_it = m_left_col_ing.begin(); ing_it != m_left_col_ing.end(); ++ing_it ) { m_all_ing.append( *ing_it ); } m_left_col_ing.empty(); for ( IngredientList::iterator ing_it = m_right_col_ing.begin(); ing_it != m_right_col_ing.end(); ++ing_it ) { m_all_ing.append( *ing_it ); } m_right_col_ing.empty(); current_header = header; return true; } else return false; } void MMFImporter::putDataInRecipe() { for ( IngredientList::const_iterator ing_it = m_left_col_ing.begin(); ing_it != m_left_col_ing.end(); ++ing_it ) m_all_ing.append( *ing_it ); for ( IngredientList::const_iterator ing_it = m_right_col_ing.begin(); ing_it != m_right_col_ing.end(); ++ing_it ) m_all_ing.append( *ing_it ); for ( IngredientList::iterator ing_it = m_all_ing.begin(); ing_it != m_all_ing.end(); ++ing_it ) { TQString name_and_prep = ( *ing_it ).name; int separator_index = name_and_prep.find( TQRegExp( "(;|,)" ) ); if ( separator_index != -1 ) { ( *ing_it ).name = name_and_prep.mid( 0, separator_index ).stripWhiteSpace(); ( *ing_it ).prepMethodList = ElementList::split(",",name_and_prep.mid( separator_index + 1, name_and_prep.length() ).stripWhiteSpace() ); } } //create the recipe Recipe new_recipe; new_recipe.yield.amount = m_servings; new_recipe.yield.type = i18n("servings"); new_recipe.title = m_title; new_recipe.instructions = m_instructions; new_recipe.ingList = m_all_ing; new_recipe.categoryList = m_categories; new_recipe.authorList = m_authors; new_recipe.recipeID = -1; //put it in the recipe list add ( new_recipe ); //reset for the next recipe to use these variables resetVars(); } void MMFImporter::resetVars() { m_left_col_ing.empty(); m_right_col_ing.empty(); m_all_ing.empty(); m_authors.clear(); m_categories.clear(); m_servings = 0; m_title = TQString::null; m_instructions = TQString::null; current_header = TQString::null; }