Chris Pollett>Old Classes>PIC 10a, Spring 2000>Hw4>Hw4 Solutions
//
// Program Name: MyDatabase.cpp
//
// Purpose: A simple database application. Allows users to create tables
//          add rows to existing tables and print out a table
//
// Known Bugs: switch/case and for loops
//		would have been used in a program if we had learned them yet
#include <fstream.h>
#include <iostream.h>
#include <iomanip.h>  //for setw
#include <stdlib.h>  //for exit()
//
//Global constants
//
const int gNameSize=256;  //Maximum size of a filename or character column
const int gMaxColumns=10; //Maximum number of columns allowed in a table
const int LEFT=1, RIGHT=2; //constants used in specifying left or right
			   //alignment of a field
//
// structs
//
struct ColInfo //struct used to hold data about how a column is formatted
{
	char fieldName[gNameSize]; // name of the column
	int typeNumber; // Type of column: 1= int, 2= double, 3= char string
	int width; // Number of characters wide column is
	int align; // Control whether characters aligned to left or right
	int precision; // For a column of type double says number of digits
		       // precision one has
	bool printFlag; // Controls whether this column will be printed
};
//
// Global data for current table being looked at
//
int gNumColumns; // Contains the total number of columns in the table
ColInfo gColData[gMaxColumns];//array containing for each column
			      //in current table what its ColInfo properties
			      //are
//
//Function prototypes
//
void ShowTitleAndOptions(); // Prints title screen and options available
void MainMenu(); // has main loop of program. calls above function then
		 // gets a choice, performs choice, and loops
bool MainMenuDoChoice(int choice); // used by MainMenu
void CreateTable(); // handles the creation of a new database table
void AddRow(); //asks the user for a tablename, then open that table
	       //calls GetTableData to gets the properties of its columns
	       //into gColData array. Closes file. Then opens file in
	       // append mode and allows the user to add one or more
	       // rows to the table
void PrintTable(); //asks the user for a tablename, then open that table
	       //calls GetTableData to gets the properties of its columns
	       //into gColData array. Closes file. Then for each column j
	       //asks the user if he wants to print column j when the
	       //table is printed. Sets the printFlag of gColData[j]
	       // accordingly. Then prints to the screen the desired
	       //portion of the table.
int GetChoice(int lo, int hi);
// Precondition: lo < hi have been assigned values
//
// Postcondition: has gotten a choice from the user between lo and hi
//		  and returned it
//
void GetTableData(ifstream&  table);
// Precondition: table is an opened ifstream
//
// PostCondition: has read from table the information about how its columns
//                are formatted and has stored this in the global array
//		  gColData. Has also stored the number of columns in
//  		  in gNumColumns
//
void GetAndOutputDataFromStream( istream&  in, int type, ostream&  out, bool
prt);
// Precondition: in and out have been opened. type is  either 1,2,3 and
//               prt is true or false
// Postcondition: if type is 1 has read one int from in
//		  if type is 2 has read one double from in
//		  if type is 3 has read a character string from in
//                if prt is true and the above read did not cause an eof
//                then the above data is written to out
// Function name: main()
//
// Purpose: Calls MainMenu to start program
//
// Known Bugs: none
int main()
{
	MainMenu();
	return 0;
}
// Function name: ShowTitleAndOptions()
//
// Purpose: Prints title and main menu
//
// Known Bugs: none
void ShowTitleAndOptions()
{
	cout <<endl;
	cout << "****************************************\n";
	cout << "*                                      *\n";
	cout << "*       Welcome to MyDatabase          *\n";
	cout << "*                                      *\n";
	cout << "*     A simple database application    *\n";
	cout << "*                                      *\n";
	cout << "****************************************\n";
	cout << "\n\n";
	cout << "Main Menu\n";
        cout << "=========\n\n";
	cout << "(1) Create table\n";
	cout << "(2) Add a row to table\n";
	cout << "(3) Print out table\n";
	cout << "(4) Quit\n";
}
// Function name: MainMenu
//
// Purpose: main loop for database program, draws main menu, gets
//          a menu choice from user and does that task, then loops
//
// Known Bugs: none
void MainMenu()
{
	int choice;
	bool stillRunning;
	do
	{
	   ShowTitleAndOptions();
	   choice = GetChoice(1,4);
	   stillRunning = MainMenuDoChoice(choice);
	}while(stillRunning);
}
// Function name: MainMenuDoChoice(int lo, int hi)
//
// Purpose: called by MainMenu to perform the choice from the main menu
//          that the user selected
//
// Known Bugs: none
bool MainMenuDoChoice( int choice)
{
	bool keepRunning = true;
	if(choice == 1) CreateTable();
	else if(choice == 2) AddRow();
	else if(choice == 3) PrintTable();
	else if(choice == 4) keepRunning =false;
	return keepRunning;
}
// Function name: GetChoice(int lo, int hi)
//
// Purpose: if lo < hi have been assigned values then gets a choice from
//          the user between lo and hi
//	    and returns it
//
// Known Bugs: Can screw up if a non-integer input by user
int GetChoice(int lo, int hi)
{
	int choice = lo-1;
	bool flag =true;
	do{
		cout << "\nPlease enter a number between " << lo
		     << " and " << hi << ": ";
		cin >> choice;
		if( choice <lo || choice >hi)
			cout << "Invalid choice.\n";
		else flag = false;
	}while(flag);
	return choice;
}
// Function name: CreateTable
//
// Purpose: sets up a database table so that rows can be added to it.
//	    To this a file for the table is created and information
//	    about how each column is formatted is collected from
//	    the user and stored in this file.
//
// Known Bugs: none
void CreateTable()
{
	char tableName[gNameSize];
	char fieldName[gNameSize];
	int col, count = 0, typeChoice, width;
	int align,precision, maxPrecision;
	ofstream tableFile;
	cout << "\nEnter a table name: ";
	cin >> tableName;
	//
	// Try to create table by open filename supplied above
	//
	tableFile.open( tableName);
	if(tableFile.fail())
	{	cout << "Error opening file to write in Create Table.\n";
		exit(1);
	}
	//
	// Choose number of columns in table
	//
	cout << "\nEnter a number of columns...";
	col = GetChoice(1,gMaxColumns);
	tableFile << col << "\n";
	//
	// Now for each column we get its name and how it is formatted
	//
	while( count < col)
	{
		cout << "\nEnter field name for column " << count <<": ";
		cin >> fieldName;
		tableFile << fieldName << "\n";
		cout << "\nSelect type:\n";
		cout << "============\n";
		cout << "(1) int\n";
		cout << "(2) double\n";
		cout << "(3) char string\n";
		typeChoice = GetChoice(1,3);
		tableFile << typeChoice << "\n";
		cout << "\nHow wide is this field?...";
		width = GetChoice(1,gNameSize);
		tableFile << width << endl;
		cout << "\nSelect alignment type:\n";
 		cout << "======================\n";
		cout << "(1) LEFT\n";
		cout << "(2) RIGHT\n";
		align = GetChoice(1,2);
		tableFile << align <<endl;
		if (typeChoice == 2)
		{
			cout << "Select a precision to output double\n";
			if(width <3) maxPrecision = 1;
			else maxPrecision = width - 2;
			precision = GetChoice (1,maxPrecision);
			tableFile << precision <<endl;
		}
		else tableFile << 1 <<endl;
		count++;
	}
	tableFile.close();
}
// Function name: GetTableData(ifstream & table)
//
// Purpose: reads from table the total number of columns stored in
//          table and how table's columns are formatted. Then stores
//          this information into the gColData array
//
// Known Bugs: none
void GetTableData(ifstream &  table)
{
	int count = 0;
	table >> gNumColumns;
	while( count < gNumColumns)
	{
		if(table.eof())
		{
		   cout << "End of file occurred while reading table data.\n";
		   exit(1);
		}
		table >> gColData[count].fieldName;
		table >> gColData[count].typeNumber;
		table >> gColData[count].width;
		table >> gColData[count].align;
		table >> gColData[count].precision;
		count++;
	}
}
// Function name: GetAndOutputDataFromStream
//
// Precondition: in and out have been opened. type is  either 1,2,3 and
//               prt is true or false
// Postcondition: if type is 1 has read one int from in
//		  if type is 2 has read one double from in
//		  if type is 3 has read a character string from in
//                if prt is true and the above read did not cause an eof
//                then the above data is written to out
//
// Known Bugs: none
void GetAndOutputDataFromStream( istream&  in, int type, ostream&  out, bool prt)
{
	char outChar[gNameSize];
	int outInt;
	double outDouble;
	if( type == 1)
	{
		in >> outInt;
		if(prt && !in.eof())
		  out << outInt;
	}
	if( type == 2)
	{
		in >> outDouble;
		if(prt && !in.eof())
		  out << outDouble;
	}
	if( type == 3)
	{
 		in >> outChar;
		if(prt && !in.eof())
		   out << outChar;
	}
}
// Function name: AddRow
//
// Purpose: asks the user for a table. Open that table to get the column
//          information. Then allows the user to append one or more rows
//          to the table using that column formatting information.
//
// Known Bugs: none
void AddRow()
{
	char tableName[gNameSize];
	ofstream tableFile;
	ifstream infoFile;
	int count;
	int tmp;
	cout << "\nEnter a table name:";
	cin >>tableName;
	infoFile.open(tableName);
	if(infoFile.fail())
	{	cout << "Error opening file to read in AddRow.\n";
		exit(1);
	}
	GetTableData(infoFile);
	infoFile.close();
	tableFile.open( tableName, ios::app);
	if(tableFile.fail())
	{	cout << "Error opening file to append in AddRow.\n";
		exit(1);
	}
	do
	{
	  count = 0;
	  while(count < gNumColumns)
	  {
		tmp = gColData[count].typeNumber;
		cout << "\nField" << count <<": "<<gColData[count].fieldName;
		cout << "\nPlease enter ";
		if( tmp == 1)
		{
		  cout << "an integer";
		}
		if( tmp == 2)
		{
		  cout << "a double";
		}
		if( tmp == 3)
		{
		  cout << "a character string";
		}
		cout << " value for this field: ";
		GetAndOutputDataFromStream( cin, tmp, tableFile, true);
		tableFile << "\n";
		count++;
	   }
	  cout << "\n\nWould you like to enter another row?\n";
	  cout << "(1) Yes \n";
	  cout << "(2) No \n";
	}while( GetChoice(1,2) == 1);
	tableFile.close();
}
// Function name: PrintTable
//
// Purpose: allows user to select a table. Then gets the column formatting info
//          for that table. Then asks user for the columns that they want to
//          print. Then prints out the table restrcited to those columns
//
// Known Bugs: none
void PrintTable()
{
	char tableName[gNameSize];
	ifstream tableFile;
	int count=0;
	cout << "\nEnter a table name:";
	cin >>tableName;
	tableFile.open(tableName);
	if(tableFile.fail())
	{	cout << "Error opening file to read in PrintTable.\n";
		exit(1);
	}
	GetTableData(tableFile);
	while( count < gNumColumns)
	{
		cout << "\nDo you want to print the field "
		     << gColData[count].fieldName << "?\n";
		cout << "(1) Yes \n";
		cout << "(2) No ";
		gColData[count].printFlag = 1-(GetChoice(1,2) -1);
		count++;
	}
	count =0;
	cout <<"\n\n";
	while (count < gNumColumns)
	{
		if(gColData[count].printFlag == true)
		{
		  cout.setf(ios::left);
		  cout << setw(gColData[count].width)
		       <<gColData[count].fieldName << "|";
		}
		count++;
	}
	cout <<endl;
	count =0;
	while(!tableFile.eof())
	{
		if(gColData[count].printFlag)
		{
		  cout.setf(ios::fixed);
		  cout.setf(ios::showpoint);
		  cout.precision(gColData[count].precision);
		  cout.width(gColData[count].width);
		  if(gColData[count].align == LEFT)
		  {
			cout.unsetf(ios::right);
			cout.setf(ios::left);
		  }
		  else
		  {
			cout.unsetf(ios::left);
			cout.setf(ios::right);
		  }
                }
		GetAndOutputDataFromStream( tableFile,
			gColData[count].typeNumber, cout,
			gColData[count].printFlag);
		if(gColData[count].printFlag && !tableFile.eof())
			cout << "|";
		count++;
		count %= gNumColumns;
		if(count == 0 ){
			 cout <<endl;
		}
	}
	cout <<"\n\n";
	tableFile.close();
}