CS350 - Notes

Week 1 - Review of Data Abstraction and C++ Modules

computer science - study of algorithms and data structures, including (1) their formal properties, (2) their linguistic and mechanical realizations, and (3) their applications.

ABSTRACTION: Reasoning about the essential properties of an object, ignoring unimportant details.

GENERALITY: The extent to which a device can be applied to many similar tasks rather than a few specific tasks.

data type - set of values that a given variable is permitted to take

e.g. integer - -32,768 to +32,767

abstract data type (ADT) - data type definition plus specification of the operations that are valid for the new type (ex. integer with assignment, arithmetic and comparison operators)

use ADTs like stacks, queues, trees, graphs, sets

data structure - way of representing or implementing an ADT

reduce complexity by information hiding (encapsulation or packaging)

INFORMATION HIDING / ENCAPSULATION: Embedding the details of a program element inside the element in such a way that when it is applied, its inner details do not have to be known. Ideally the inner details are so well hidden that not only are they not needed, they are not even available (usually thought of as "invisible" to the outside).

SPECIFICATION: A detailed description of the essential properties of an object. In programming, a complete description of a program unit's required behavior.

IMPLEMENTATION: An object built to satisfy a specification. In programming, a program unit written to behave as specified. Not all behavior is specified, which gives the implementor some flexibility in deciding implementation specific behavior.

INTERFACE: The way in which an object interacts with its external environment. In programming, a description of how a programming unit is invoked from its external program environment (e.g., parmeter passing conventions) along with a list of any dependencies that exist between the two (e.g., global variables used by the unit).

BLACK BOX MODEL: Viewing an object in terms of its interface, ignoring all internal details, pretending that the internal environment is co pletely hidden inside a black box. In programming, viewing a program element in terms of its interface while ignoring its inner details (e.g., local variables and algorithms).

MODULE: A separately compiled program unit that can be used to collect related constants, types, variables and subprograms in such a way that any program or module can access module elements that have been designated public, while only the source module can access module elements that have been designated private.

THE Header File: C++'S MECHANISM FOR ADTs

library module encapsulates types, variables, constants and procedures

Data Abstraction and Objects

OBJECT-ORIENTED DESIGN AND PROGRAMMING

" The future of computing is in object-oriented programming "
Nobel Laureate Kenneth Wilson

OOPs is a shift from passive data structures that are manipulated by procedures into active units that manipulate themselves (Brookshears)

" Object-oriented programming is a programming paradigm that encapsulates both data and procedures (methods) within an object. Objects are activated by messages. This paradigm emphasizes the interface between objects rather than the content of objects, encouraging the development of modular, interchangable units of code. True OO systems also support inheritance in the context of class hierarchies, enabling programmers to describe descendent objects efficiently by describing only the differences between parent and child objects. " Interface, 1990

"What makes programming object-oriented? Mainly a linking technique whereby common characteristics or traits can be 'inherited' or passed from one programming module or 'object' to another." Interface, 1990

"Object-oriented programming is hailed as the keystone to the future of computer programming. A number of reasons are often cited. the prinicpal ones are an ability to generate re-usable code--a long-held programmer's dream--and a reduction in lines of computer coding--in some cases a 50 to 75 percent savings in programming time and effort have been realized." Interface, 1990

Definition An object is a bundle of variables and behaviors. An object has a type, which is called its class. An object's class defines the particular variables that it contains and the behaviors that it can exhibit. The variables associated with a class are called instance variables and the behaviors are called methods. Methods are the processes that can be activated within an object-oriented design. Thus, methods correspond to the functions and procedures of a function-oriented design.

Definition An object-oriented design methodology has four major steps:

  1. Identify the object types (classes) that are part of the solution.
  2. Identify and describe the instance variables that are contained within each type of object.
  3. Identify each of the methods that can act upon each type of object.
  4. For each method, describe its purpose, parameters, preconditions and postconditions.
OBJECT-ORIENTED PROGRAMMING AND THE SIX DESIGN PRINCIPLES

Problem Decomposition: natural decomposition

Formal Specifications: document with pre- and post-conditions

Functional Cohesion: automatic

Communicational cohesion - all parts of a routine are related to a common collection of data

Loosely Coupled and Explicit Interfaces: data is static and messages are passed

Information Hiding: automatic

Chapter 2 - Is This Really Programming

C++ Builder program - application framework

events (stimulus) and functions (response)

How Much Programming is there to do?

Building Your Own Components in C++ Builder (Code it once, use it forever)

BigThink versus LittleThink versus MiddleThink
Top-down design with stepwise refinement (BigThink)
Bottom-up programming with modularization (LittleThink)

C++ Builder - MiddleThink: worrying about putting components together and the edges in-between

Hints to studying C++ Builder:

Learn by doing

Don't try to be a C++ wizard (C++ Builder is a different approach to programming)

Learn one component at a time, and learn it wholly

Examine Borland's example applications

Exile the bulk of your C++ code to separate units apart from your form units

Don't try to create your own C++ Builder components before you completely understand how to use them

Use your right brain


Week 3 - C++ Data Types

Chapter 4 - All of Your Stuff -- And Buckets to Put It In

variables - "buckets for data"

initializing variables

literal constants vs. named constants

data types: short integer (-32768 -> 32767)
int, longint (-2147483648 -> 2147483647)
char (-128-127)
float (1.17 x 10-38 -> 3.4 x 1038) 6 significant figures
double (2.22 x 10-308 -> 1.79 x 10308) 15 sig. fig.
long double (3.4 x 10-4932 -> 1.18 x 104932) 18 sig. fig
bool
String

arithmetic operators: +, -, *, /, %

comment delimiters //, /* */

Strings

String TextData;
TextData = "Welcome to NMHU";
Label1->Caption = TextData;
String1 = "Welcome to";
String2 = "NMHU";
WelcomeMessage = String1 + String2;
MessageSize = WelcomeMessage->Length();

C++ Builder still supports null terminated strings (arrays of characters) with strcpy, strlen, & strcat

bool AllDone;
AllDone = false;
AllDone = (Counter == 0);

Relational operators: =, >, <, <>, <=, >=

Simple & Compound IF statements


Chapter 6 - Data, Data, Everywhere

Defining Your Own Data Types

type
class TForm1 : TForm
{    __published:  // IDE-managed Components
   private:   //User private declarations
  public:   // User public declarations
    virtual __fastcall TForm1(TComponent* Owner);
};

Enumerated Types

enum TFormBordersStyle = (bsNone, bsSingle, bsSizeable, bsDialog, bsToolWindow, bsSizeToolWin);

TFormBorderStyle Myborder;

MyBorder = bsSingle;
if (MyBorder == bsSizeable) then

Incrementing, Decrementing and Interation

c++;
c--;

Preincrement vs. Postincrement

for Statements

for (<intialization statement> ; <conditional statement>; <increment statement>)
<statement>

See ROOTDEMO on CD

void __fastcall TForm1::FormCreate(TObject *Sender)
//procedure which fills a ListBox with a square root table}
{
   int count;
   float nbr;
   char realText;

   for (count = 1; count <= 100; count++)
   {
     nbr = sqrt(count);
     sprintf (realText, "%3d: %14.9f", count, nbr);
     ListBox1->Items->Add(realText);
  }
}

Display Formatting

can't assign a numeric value to the Caption property of a label
requires sprintf
sprintf (s, "Hello, world");
sprintf (s, "%3d: %14.9f", i , sqrt (i));

Rounding for Display

sprintf (s, "%5.2f", 17.005);

ARRAYS

typedef char Chars1D[12];

Chars1D Array1D;

Array1D[4] = 'Q';
Array1D[2] = Array1D[4];

for (count = 0; count <= 11; count++)
  Array1D[count] = (char)('a' + count);

typedef char Chars2D[12,4];

Structures

struct TStudent
{
   char ID[6];
   char Name[20];
  char Major[10];
  float GPA;
  int Age;
}

TStudent Student1;

Student1.ID = "12345";
Student1.GPA = 3.14;


Week 5/6 - Objects

Chapter 8 - Units and Objects

The Great Dichotomy - code vs. data

Packages of Computation (Functions)

void __fastcall TMainform::FileOpenItemClick(TObject *Sender)
{
  String FileExt;
  if OpenDialog->Execute()
  {
    FileExt = UpperCase(ExtractFileExt (OpenDialog->Filename));
    if (FileExt == ".BMP") || (FileExt == ".ICO") || (FileExt == ".WMF")
      CreatePictureViewer(OpenDialog->Filename);
    else
      CreateTextViewer(OpenDialog->Filename);
  }
}

Units: Packages of Packages

interface (.H file), implementation (.CPP unit file), includes clause

#include    -   standard units
#include "ViewMain.h"   -   user defined units

#ifndef __MORTUNIT_H
#define __MORTUNIT_H
/*-------------------------------------------------*/
/* MORTUNIT */
/* By Jeff Duntemann KG7JF */
/* */
/* A mortgage calculation "engine" for C++ Builder. */
/* Written for *C++ Builder Programming Explorer* */
/* Copyright (c) 1995 The Coriolis Group, Inc. */
/* */
/* Adapted loosely from code originally published */
/* in DDJ for October 1991 -- Last Updated 2/26/95 */}
/*-------------------------------------------------- */


struct TLender
{
  int LenderID;
  String LenderName;
  String LenderAddress;
  String LenderCity;
  String LenderState;
  String LenderZIP;
  String LenderPhone;
};

struct TPayment // One element in the amortization table.
{
  int PaymentNum;
  float PayPrincipal;
  float PayInterest;
  float PrincipalSoFar;
  float InterestSoFar;
  float ExtraPrincipal;
  float Balance;
};

typedef TPayment PaymentArray[600];

struct TMortgage
{
  TLender Lender; // Lender information on a loan
  int Periods; // Number of periods in mortgage
  int PeriodsPerYear; //Number of periods in a year
  float Principal; //Amount of principal in cents
  float Interest; //Percentage of interest per *YEAR*
  float MonthlyPI; //Monthly payment in cents
  PaymentArray Payments; //Array of payment records
}

void InitMortgage ( TMortgage & ThisMortgage, float StartPrincipal, float StartInterest, int StartPeriods, int StartPeriodsPerYear );

void SetNewInterestRate ( TMortgage & ThisMortgage, float NewRate );

void Recalc ( TMortgage & ThisMortgage );

void ApplyExtraPrincipal ( TMortgage & ThisMortgage, int PaymentNumber, float Extra );

void RangeExtraPrincipal ( TMortgage & ThisMortgage, int StartPayment, int EndPayment, float Extra );

void MultipleExtraPrincipal ( TMortgage & ThisMortgage, int StartPayment, float Extra );

void RemoveExtraPrincipal ( TMortgage & ThisMortgage, int PaymentNumber );

void RemoveAllExtraPrincipal ( TMortgage & ThisMortgage );

#endif


/*******************************************************/
/* MORTUNIT */
/* By Jeff Duntemann KG7JF */
/* */
/* A mortgage calculation "engine" for C++Builder. */
/* This is the unit- rather than object-based version */
/* Written for "The C++Builder Programming Explorer" */
/* Copyright © 1996, The Coriolis Group, Inc. */
/* Converted from the original version written for */
/* *The New Delphi 2 Programming Explorer* */
/* */
/* Adapted loosely from code originally published */
/* in DDJ for October 1991 -- Last Updated 1/9/96 */
/*-----------------------------------------------------*/
#include "MortUnit.h"
#include

static float CalcPayment
   (
   float Principal,
   float InterestPerPeriod,
   int NumberOfPeriods
) { float Factor; Factor = pow ((1.0 + InterestPerPeriod), -NumberOfPeriods); return (Principal * InterestPerPeriod)/(1.0 - Factor); } void InitMortgage ( TMortgage & ThisMortgage, float StartPrincipal, float StartInterest, int StartPeriods, int StartPeriodsPerYear ) { // Set up all the initial state values in the TMortgage structure ThisMortgage.Principal = StartPrincipal; ThisMortgage.Interest = StartInterest; ThisMortgage.Periods = StartPeriods; ThisMortgage.PeriodsPerYear = StartPeriodsPerYear; // and then build the mortgage table Recalc (ThisMortgage); } void SetNewInterestRate ( TMortgage & ThisMortgage, float NewRate ) { ThisMortgage.Interest = NewRate; Recalc (ThisMortgage); // Re-amortize the mortgage and rebuild the table } // This routine calculates the amortization table for the mortgage. void Recalc ( TMortgage & ThisMortgage ) { float RemainingPrincipal; int PayNum; float InterestThisPeriod; float InterestPerPeriod; float HypotheticalPrincipal; InterestPerPeriod = ThisMortgage.Interest/ThisMortgage.PeriodsPerYear; ThisMortgage.MonthlyPI = CalcPayment (ThisMortgage.Principal, InterestPerPeriod, ThisMortgage.Periods); // Round the monthly to cents ThisMortgage.MonthlyPI = floor (ThisMortgage.MonthlyPI * 100 + 0.5) / 100; // Now generate the amortization table RemainingPrincipal = ThisMortgage.Principal; // Do this for each period (that is, payment) in the loan for (PayNum = 1; PayNum <= ThisMortgage.Periods; PayNum++) { TPayment * pmt; // Calculate the interest this period and round it to cents InterestThisPeriod = floor (RemainingPrincipal * InterestPerPeriod * 100 + 0.5) / 100; // Store values into current payment structure in payment array pmt = &(ThisMortgage.Payments[PayNum]); pmt->PaymentNum = PayNum; // The payment number, from 1 if (RemainingPrincipal == 0) { // Loan's been paid off!! pmt->PayInterest = 0; pmt->PayPrincipal = 0; pmt->Balance = 0; } else { // There's still principal to repay HypotheticalPrincipal = ThisMortgage.MonthlyPI - InterestThisPeriod + pmt->ExtraPrincipal; if (HypotheticalPrincipal > RemainingPrincipal) pmt->PayPrincipal = RemainingPrincipal; else pmt->PayPrincipal = HypotheticalPrincipal; pmt->PayInterest = InterestThisPeriod; // Update running balance RemainingPrincipal = RemainingPrincipal - pmt->PayPrincipal; pmt->Balance = RemainingPrincipal; } // Update the cumulative interest and principal fields if (PayNum <= 1) // First payment; so cumulative values { // are simply the current values pmt->PrincipalSoFar = pmt->PayPrincipal; pmt->InterestSoFar = pmt->PayInterest; } else // We're not at first payment, so we must reach back to { // previous payment to calculate cumulative totals pmt->PrincipalSoFar = ThisMortgage.Payments[PayNum-1].PrincipalSoFar + pmt->PayPrincipal; pmt->InterestSoFar = ThisMortgage.Payments[PayNum-1].InterestSoFar + pmt->PayInterest; } } } void ApplyExtraPrincipal ( TMortgage & ThisMortgage, int PaymentNumber, float Extra ) { ThisMortgage.Payments[PaymentNumber].ExtraPrincipal = Extra; Recalc(ThisMortgage); //Re-amortize the mortgage and rebuild the table } void RemoveExtraPrincipal ( TMortgage & ThisMortgage, int PaymentNumber ) { ThisMortgage.Payments[PaymentNumber].ExtraPrincipal = 0.0; Recalc(ThisMortgage); // Re-amortize the mortgage and rebuild the table } void RemoveAllExtraPrincipal ( TMortgage & ThisMortgage ) { int i; for (i = 0; i <= ThisMortgage.Periods; i++) { ThisMortgage.Payments[i].ExtraPrincipal = 0.0; } Recalc(ThisMortgage); //Re-amortize the mortgage and rebuild the table } void MultipleExtraPrincipal ( TMortgage & ThisMortgage, int StartPayment, float Extra ) { RangeExtraPrincipal (ThisMortgage, StartPayment, ThisMortgage.Periods, Extra); } void RangeExtraPrincipal ( TMortgage & ThisMortgage, int StartPayment, int EndPayment, float Extra ) { int i; for (i = StartPayment; i <= EndPayment; i++) { ThisMortgage.Payments[i].ExtraPrincipal = Extra; } Recalc (ThisMortgage); // Re-amortize the mortgage & rebuild the table. // Here, we clear out any unnecessary extra principal values for (i = 1; i <= ThisMortgage.Periods; i++) { if ((ThisMortgage.Payments[i].Balance <= 0.00) || (ThisMortgage.Payments[i].Balance < Extra)) ThisMortgage.Payments[i].ExtraPrincipal = 0.00; } }


The Header File

declares functions and data members for objects

to call a function, use its name and parameter list:
InitMortgage (MyMortgage, 100000.0, 0.085, 360, 12);

Using Code and Data Stored in Units

#include <vcl\Classes.hpp>
#include "MortUnit.h"

The Unit file

  1. Function bodies implementing the functions declared in the header file
  2. Definitions private to the unit as a whole

Implementing a Mortgage Engine


InitMortgage
Recalc
CalcPayment (note - hidden file)

Attaching a User Interface to a Unit-Based Engine

Use stringgrid (table) and button
//---------------------------------------------------------------------------
#ifndef UnitTestH
#define UnitTestH
//---------------------------------------------------------------------------
#include <vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
#include <vcl\Grids.hpp>
#include "MortUnit.h"   // our mortgage engine unit
//---------------------------------------------------------------------------
class TUnitTestForm : public TForm
{
__published:	// IDE-managed Components
        TStringGrid *UnitTestGrid;
        TButton *Button1;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall FormCreate(TObject *Sender);
private:	// User declarations
        TMortgage Mortgage;
public:		// User declarations
        virtual __fastcall TUnitTestForm(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern TUnitTestForm *UnitTestForm;
//---------------------------------------------------------------------------
#endif
/*
 This is the unit-based version of the example program.
 Updated for C++Builder on 01/01/97.  Happy New Year!
 Copyright © 1997 by The Coriolis Group, Inc.
*/
//---------------------------------------------------------------------------
#include 
#pragma hdrstop

#include "UnitTest.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TUnitTestForm *UnitTestForm;
//---------------------------------------------------------------------------
__fastcall TUnitTestForm::TUnitTestForm(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TUnitTestForm::Button1Click(TObject *Sender)
{
  Close ();
}
//---------------------------------------------------------------------
// When the form is created on program startup, all this happens
void __fastcall TUnitTestForm::FormCreate(TObject *Sender)
{
  int i;

  InitMortgage (Mortgage,
                100000.00,  // initial principal
                0.07,       // Interest rate as a decimal fraction, not % !
                360,        // Periods in loan
                12);        // Periods per year

  // Initialize the string grid
  // We need one row per payment plus a header row at the top
  UnitTestGrid->RowCount = Mortgage.Periods + 1;

  // Fill in the header row with the column caption strings
  UnitTestGrid->Cells[0][0] = "Payment #";
  UnitTestGrid->Cells[1][0] = "Principal";
  UnitTestGrid->Cells[2][0] = "Interest";
  UnitTestGrid->Cells[3][0] = "Prin. So Far";
  UnitTestGrid->Cells[4][0] = "Int. So Far";
  UnitTestGrid->Cells[5][0] = "Extra Prin.";
  UnitTestGrid->Cells[6][0] = "Balance";

  // next we transfer the data from the mortgage structure
  // to the string grid on the form
  for (i = 1; i <= Mortgage.Periods; i++)
  {
    char TempString[20];
    /*
      To put the mortgage table into the string grid we must
      convert the numeric values in the mortgage table
      to strings.  That's what this messiness is for.
    */
    sprintf (TempString, "%d", i);
    UnitTestGrid->Cells[0][i] = TempString;

    sprintf (TempString, "%.2f", Mortgage.Payments[i].PayPrincipal);
    UnitTestGrid->Cells[1][i] = TempString;

    sprintf (TempString, "%.2f", Mortgage.Payments[i].PayInterest);
    UnitTestGrid->Cells[2][i] = TempString;

    sprintf (TempString, "%.2f", Mortgage.Payments[i].PrincipalSoFar);
    UnitTestGrid->Cells[3][i] = TempString;

    sprintf (TempString, "%.2f", Mortgage.Payments[i].InterestSoFar);
    UnitTestGrid->Cells[4][i] = TempString;

    sprintf (TempString, "%.2f", Mortgage.Payments[i].ExtraPrincipal);
    UnitTestGrid->Cells[5][i] = TempString;

    sprintf (TempString, "%.2f", Mortgage.Payments[i].Balance);
    UnitTestGrid->Cells[6][i] = TempString;
  }
}
//---------------------------------------------------------------------

From Structures to Objects

unit - physical package existing as a disk file
object - logical package existing in computer memory

class TPayment : public TObject // One element in the amortization table
{
public:
  int PaymentNum;
  float PayPrincipal;
  float PayInterest;
  float PrincipalSoFar;
  float InterestSoFar;
  float ExtraPrincipal;
  float Balance;
  virtual __fastcall TPayment (void);   //constructor
  void __fastcall GetStringForm (String NumberStrings[]);
};

class TMortgage : public TObject
{
public:
  TLender Lender;         // Lender information on a loan
  int Periods;            // Number of periods in mortgage
  int PeriodsPerYear;     // Number of periods in a year
  float Principal;        // Amount of principal in cents
  float Interest;         // Percentage of interest per *YEAR*
  float MonthlyPI;        // Monthly payment in cents
  TList * Payments;       // List object holding payments

  virtual __fastcall TMortgage
    (
      float StartPrincipal,
      float StartInterest,
      int StartPeriods,
      int StartPeriodsPerYear
    );
  virtual __fastcall ~TMortgage (void);

  void __fastcall SetNewInterestRate (float NewRate);
  void __fastcall Recalc (void);
  TPayment * __fastcall GetPayment (int PaymentNumber);
  void __fastcall ApplyExtraPrincipal
      (int PaymentNumber, float Extra);
  void __fastcall RangeExtraPrincipal
      (int StartPayment, int EndPayment, float Extra);
  void __fastcall MultipleExtraPrincipal
      (int StartPayment, float Extra);
  void __fastcall RemoveExtraPrincipal (int PaymentNumber);
  void __fastcall RemoveAllExtraPrincipal (void);
};

UNITS AND OBJECTS

void __fastcall TMortgage::RemoveAllExtraPrincipal (void)
{
  int i;

  for (i = 0; i <= Periods; i++)
  {
    TPayment * pmt;

    pmt = reinterpret_cast<TPayment *>(Payments->Items[i]);
    pmt->ExtraPrincipal = 0.0;
  }

  Recalc(); //Re-amortize the mortgage and rebuild the table
}

INHERITING CREATION

All VCL classes descend from TObject

Creating Objects

AClass *MyObjectInstance;
MyObjectInstance = new AClass;

__fastcall TPayment::TPayment (void) : TObject ()
{
}

// The constructor initializes the mortage object's fields
__fastcall TMortgage::TMortgage
      (
        float StartPrincipal,
        float StartInterest,
        int StartPeriods,
        int StartPeriodsPerYear
      ) : TObject ()
{
  int i;

  // Set up all the initial state values in the TMortgage object
  Principal = StartPrincipal;
  Interest = StartInterest;
  Periods = StartPeriods;
  PeriodsPerYear = StartPeriodsPerYear;

  Payments = new TList;  // all objects must be created with new

  // We configure the Payments TList to expand to the exact size we need
  Payments->Capacity = Periods;

  // Create and add Period empty payment objects to TList
  for (i = 0; i <= Periods; i++)
  {
    Payments->Add (new TPayment);
  }

  // and then build the mortgage table
  Recalc ();
}

Destroying Objects

delete MyObjectInstance;

__fastcall TMortgage::~TMortgage (void)
{
  delete Payments;
}

AN OBJECT-ORIENTED MORTGAGE ENGINE

see MORTLIB.H and MORTLIB.CPP on the CD

Using the TList Object

Payments = new TList;

Payments->Capacity = Periods;

for (i = 0; i <= Periods; i++)
{
  Payments->Add (new TPayment);
}

TPayment *pmt;
// Store values into current payment object in payments list
pmt = reinterpret_cast(Payments->Items[PayNum]);

The String Data Type

String MyString;
MyString = "Hello, world";

String MyString ("Hello, world");
String MyNumber (12.5); //creates a string and intializes it with "12.5"


CS305 Week 7/8 - Objects (Encapsulation, Inheritance & Polymorphism)

Chapter 10 - Our Objects All Sublime


OOPs vs. Structured Programming

Encapsulation - binding code and data into a single structure while hiding the implementation details

information hiding:

void __fastcall TMortgage::SetPeriods (int NewPeriodsValue)
{
  Periods = NewPeriodsValue;
}

int __fastcall TMortgage::GetPeriods (void)
{
  return Periods;
}

important if nature of data may change and don't want users to depend on a particular implementation of the data structures

MODEL DATA FIRST (Object-oriented design from week 2)

Inheritance
The ability to create new objects that maintain the properties & behaviors of ancestor objects

class TPayment : public TObject

TObject Methods common to all objects
TPersistent ability to write itself to EXE file and bringitself back at runtime
TControl ability to interact with the user
TWinControl ability to use Windows machinery to create a window
TButtonControl handles button-style resonses
TButton provides "real" button features (pushing,changing surfaces)

All but TButton are "abstract" classes

TWinControl
TScrollingWinControl
TForm
TForm1

class TUnitTestForm : public TForm
{
__published:	// IDE-managed Components
        TStringGrid *UnitTestGrid;
        TButton *Button1;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall FormCreate(TObject *Sender);
        
        void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:	// User declarations
        TMortgage * Mortgage;
public:		// User declarations
        virtual __fastcall TUnitTestForm(TComponent* Owner);
};

A portion of C++Builder's object hierarchy
  TObject  
  TPersistent TPrinter
TGraphic TCanvas TControl
TBitmap TControlCanvas TWinControl
  TScrollingWinControl TButtonControl
  TForm TButton
  TMyForm  

Inherit everything you can.

Add or change only what you must.

Polymorphism - calls to methods of an object variable will call code appropriate to whatever instance is actually in the variable.

Anatomy of an Object

An object is an instance of a class that wraps up data and code all into one bundle. Before OOP, code and data were treated as separate elements.

An object is a specific instance of a class. A class is a collection of related information that includes both data and functions (the instructions for working with that data). The properties of C++Builder objects have default values. You can change these values at design time to modify an object to suit the needs of your project without writing code. If you want a property value to change at runtime, you need to write only a very small amount of code.

C++Builder applications are based on the property-method-event (PME) model of programming. Applications are built using discrete software components. These components have properties that define the component’s state. The properties can be changed to affect the components. The components also have built in methods, or member functions, which can be used to manipulate the component. Components trigger events when changes occur in the component. These events are passed by calling special event-handling properties set by the code using the component.

C++Builder’s Visual Component Library (VCL) now allows C++ programmers to do visually what they have traditionally done by hand coding classes or by coding using an application framework such as Microsoft Foundation Class (MFC) or ObjectWindows Library (OWL). Most of the pieces that make up a Win32 application are encapsulated in the VCL library. These pieces include:

Because all these features are now available through the VCL, and most can be manipulated visually through the C++Builder user interface, C++ programmers no longer need to create or manipulate these objects through code. Many C++Builder applications can be created by designing the application visually using the C++Builder Form Designer and adding a few lines of code to the key component’s event handlers.

Remember this rule of thumb: use the VCL objects whenever possible and resist the urge to write new code until all other possibilities have been exhausted.

Field(field definition, instance variables) - data fields within an object

Methods are procedures or functions belonging to a given object which give an object behavior.

constructor is responsible for creating an instance of your object and allocating memory or initializing any necessary fields: Instance = ObjectType::Create(Parameters)

destructor deallocates any allocated memory and performs other required housekeeping

Method Types
TFoo = class
static void IAmAStatic;
virtual void IAmAVirtual;
end;

Static methods are the default and work like regular procedures and functions. the compiler knows the address of the method and when you call a static method, the compiler links it statically into the executable.
A static member function of a global class has external linkage. A member of a local class has no linkage. A static member function is associated only with the class in which it is declared. Therefore, such member functions cannot be virtual.

Static member functions can only call other static member functions and only have access to static data. Such member functions do not have a this pointer.

Virtual methods can be overridden. The compiler builds a Virtual Method Table (VMT) that provides a means to look up the method's address at runtime. An object's VMT contains all of it's ancestor's virtual method addresses as well as the one it declares.
Use the virtual keyword to allow derived classes to provide different versions of a base class function. Once you declare a function as virtual, you can redefine it in any derived class, even if the number and type of arguments are the same.
The redefined function overrides the base class function.

Overriding a Method
Object C++ methods can be overriden only if they are first declared as virtual.
Overriding methods means extending or refining an ancestor's method, rather than replacing it. To override a method in a descendant class, redeclare the method in the derived class, ensuring that the number and type of arguments are the same. Ex.

class TFirstComponent : public TComponent
{
public:
  void Move();                               // regular method
  virtual void Flash();                      // virtual method
};
class TSecondComponent : public TFirstComponent
{
public:
  void Move();                // declares new method "hiding" TFirstComponent::Move()
  void Flash();               // overrides virtual TFirstComponent::Flash in TFirstComponent
};

Class Visibility Specifiers

Private and Public

When you build an application using the C++Builder environment, you are adding data members and methods to a descendant of TForm. You can also add data members and methods to a class without putting components on a form or filling in event handlers, but by modifying the class declaration directly. There are private, protected, public, published, and automated declarations in a class. You can add new data members and methods to either the public or private part of a class. Public and private are keywords. When you add a new form to the project, C++Builder begins constructing the new form class. Each new class contains the public and private keywords that mark locations for data members and methods you want to add to the code directly. For example, note the private and public parts in this new form class declaration that so far contains no data members or methods:

class TForm1 : public TForm
{
__published:	// IDE-managed Components
private:	// User declarations
public:		// User declarations
	virtual __fastcall TForm1(TComponent* Owner);
};

Use the public part to:

  1. Declare data members you want methods in other classes to access
  2. Declare methods you want other classes to access

Declarations in the private part are restricted in their access. If you declare data members or methods to be private, they are unknown and inaccessible outside the class. Use the private part to:

  1. Declare data members you want only methods in the same class to access
  2. Declare methods you want only methods in the same class to access

To add data members or methods to a public, private, or protected section, put the data members or method declarations after the appropriate access specifier (public, private, and so on). Here is an example:

class TForm1 : public TForm
{
__published:	// reserved for IDE-managed Components
	TEdit *Edit1;
	TButton *Button1;
	void __fastcall Button1Click(TObject *Sender);
private:	// User declarations
	int Number;
   	int Calculate (int X, int Y);
public:		// User declarations
	virtual __fastcall TForm1(TComponent* Owner);
   	void ChangeColor();
};

Place the code that implements the Calculate and ChangeColor methods in the .cpp part of the unit file pair.
The Number data member and Calculate function are declared to be private. Only members within the class can use Number and Calculate. This restriction means that only the form class can use them. Because the ChangeColor method is declared to be public, code in other units can use it. Such a method call from another unit must include the object name in the call:
Form1->ChangeColor;
The unit making this method call must #include Form1's Unit1 file. The other declarations listed at the beginning of this section are discussed in further detail in the Programmer's Guide. In brief, the published declaration is reserved for the IDE -managed components and has the same scope as public. The IDE uses this section to maintain the component and event handlers for the form. Published properties display in the Object Inspector. Automated declarations cause automation type information to be generated for the method property and also have the same scope as public. This automation information makes it possible to create OLE Automation Servers. Protected declarations can only be seen by the current class or its descendants.

Polymorphism: Doing it Your Own Way

ex. Repaint method in TControl class

typecasting - technique where the compiler is forced to view a variable of one type as another type. [works if data size of both types the same]

Polymorphic Assignment

int nbr;
float ans;

ans = (float) nbr;

Pointers and Masks:
ex.
(reinterpret_cast<TControl *>(MyButton))->ShowHint = true;

Project POLYTEST.MAK (on disk)

void __fastcall TForm1::FormClick(TObject *Sender)
{
  (reinterpret_cast(Sender))->Hint = "I'm hosed!";
//  (reinterpret_cast(Sender))->Repaint ();
}

same procedure then copied to other objects' OnClick event handlers

Polymorphic Execution
replace statement above with
(reinterpret_cast<TControl *>(Sender)->Repaint();

POLYMORPHISM AND THE GRAND DESIGN

* Objects are active data.

* Objects can inherit both data and code from existing objects.

* Polymorphic assignment and execution allow you to work on objects of different and unpredictable classes farther up the object hierarchy, where they have a common ancestor class containing common fields and methods.


CS305 Week 9/10

Chapter 11 - Files and File Types


Opening and Closing Files

Opening Streams

ifstream infile ("textfile.txt"); // declares stream variable and opens file

ifstream * infile; //declare a pointer to a stream variable
infile = new ifstream ("textfile.txt"); // create the stream and open the file

ifstream infile; //declare stream variable
infile.open ("textfile.txt"); // open file

ifstream infile;
infile.open ("textfile.txt");
if (infile.fail ())
{
  // open failed. display error message and return
}
// open successful. continue processing

if (!infile)
{
  // open failed. display error message and return
}

What Happens When You Open a File?

use a second parameter for open --
enum open_mode	{
	app,			//Append data--always write at end of file.
	ate,			// Seek to end of file upon original open.
	in,				// Open for input (default for ifstream).
	out,			// Open for output (default for ofstream).
	binary,			// Open file in binary mode.
	trunc,			// discard content if file exists (default if out is specified and not app or ate
	nocreate,		// if file does not exist, open fails
	noreplace		// if file exists, open for output fails unless ate or app
};

Closing Streams

infile.close();

Text Files

sequential

Writing Text files

outfile << 'c';
if a new object is defined, you will need to overload the << operator
outfile << "Hello, " << Name << ". How are you today?" << endl;

Formatting Output

outfile << setw(10) << Number1 << setw(10) << Number2 << endl;
setw left-justifies numbers and right-justifies strings; In order for strings to be left-justifed, you need to add:
outfile.setf(ios::left, ios::adjustfield);

Reading Text Files

infile >> Number1;
infile >> Number1 >> Number2 >> Number3;
infile.getline (str, 80);

Storing Binary Data

//statistics record for each one-hour period
struct AlarmStatsRecord
{
char Day[4];
char Time[6];
int NumberOfSignals;
int OperatorSignals;
int AlarmSignals;
int Troublesignals;
};

Writing and Reading Records

AlarmStatsRecord StatsRecord;
fstream StatsFile;

//open the stream and fill in the stats record
StatsFile.write ((char *)&StatsRecord, sizeof (StatsRecord));

StatsFile.read ((char *)&StatsRecord, sizeof (StatsRecord));

Detecting End Of File

for streams, use eof:
while (!infile.eof ())
{
	infile.get (c);
	outfile.put(c);
}

The File Pointer and Record Numbers

Moving and Examining The File Pointer

StatsFile.seekg (4 * sizeof (StatsRecord)); // seek to the 5th record counting from 0
StatsFile.read ((char *)&StatsRecord, sizeof (StatsRecord)); // and read the record

StatsFile.seekp (4 * sizeof (StatsRecord)); // seek to the 5th record counting from 0
StatsFile.read ((char *)&StatsRecord, sizeof (StatsRecord)); // and read the record
StatsRecord.NumberOfSignals = 100;
StatsFile.seekp (-sizeof (StatsRecord), ios:cur); // move file pointer back 1
StatsFile.write ((char *)&StatsRecord, sizeof (StatsRecord)); // and write the record

//seek to the end of the file before appending records
StatsFile.seekp (0, ios::end);

FileSize = StatsFile.tellg();


Chapter 12 - Points of Light, Spots on Paper


canvas of type TCanvas

TColor TestColor;
Integer X, Y;
{
Canvas->Pixels[X,Y] = clWhite;
TestColor = Canvas->Pixels[X,Y];

TCanvas->PenPos property always starts at 0,0

void __fastcall TForm1::Button1Click (TObject *Sender)
{
   Canvas->MoveTo(0,0);
   Canvas->LineTo(ClientWidth, ClientHeight);
   Canvas->MoveTo(ClientWidth, 0);
   Canvas->LineTo(0, ClientHeight)
}

Clearing a Canvas
Refresh();

TPen Object
Pen.Color
Pen.Width
Pen.Style (psSolid, psDot, psClear, ...)

Simple Figures

Bounding Rectangles: FIGMAKER.CPP
void __fastcall TForm1::FormPainter (TObject *Sender)
{
  Width = 490;
  Height = 310;

  Canvas->Rectangle (10, 10, 170, 80);

  Canvas->Rectangle (10, 100, 170, 170);
  Canvas->Ellipse(10, 100, 170, 170);

  Canvas->Ellipse(10, 190, 170, 260);

  Canvas->Rectangle (200, 10, 370, 80);
  Canvas->RoundRect (200, 10, 370, 80, 20, 20);

  Canvas->RoundRect (200, 10, 370, 170, 20, 20);

  Canvas->Rectangle (400, 10, 470, 80);
  Canvas->Ellipse(400, 10, 470, 80);

  Canvas->Ellipse(400, 100, 470, 170);

  Canvas->RoundRect (200, 190, 370, 260, 50, 20);
}

Polylines and Polygons


Open Array Parameters

void __fastcall TForm1->FormPaint (TObject *Sender)
{
  Canvas->Brush->Color = clYellow;
  Canvas->Polygon( OPENARRAY (Point(150,20), Point(260,100), Point(220,220), Point(80,220),Point(40,100)));
  Canvas->Polyline(OPENARRAY(Point(110,160), Point(110,100), Point(150,160), Point(190,100), Point(190,160)));
}


Printing Graphics

#include Printer; declares TPrinter


BeginDoc
  :
EndDoc


specify printer using PrinterDialogSetup1->Execute() dialog

specify Printer->PageWidth & Printer->PageHeight & Printer->Orientation

try..catch And Printing

see PTEST.MAK on CD-ROM
to print multiple pages, use Printer->NewPage

PROJECT - SpiroMania

SPINNER.H

//---------------------------------------------------------------------------
#ifndef spinnerH
#define spinnerH
void SpinWheels(TCanvas * TargetCanvas,
                int A, int B, int D,
                int CenterX, int CenterY,
                TColor DrawColor);
//---------------------------------------------------------------------------
#endif

SPINNER.CPP

//---------------------------------------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop

#include <math.h>
#include "spinner.h"
//---------------------------------------------------------------------------
//                   SpiroMania
//      SPINNER.CPP : Cycloid Curve Generator
//            By Jeff Duntemann KG7JF
//
// The cycloid curve generator shown here generates
// patterns similarly to the Kenner SpiroGraph toy.
// Written for *The New Delphi 2 Programming Explorer*
// Converted to C++ for *The C++Builder Programming Explorer*
// Copyright (c) 1996,1997 The Coriolis Group, Inc.
//              Last Updated 1/13/97
//---------------------------------------------------------------------------

static int HighestCommonFactor (int a, int b)
{
  int i, j, hcf;

  // Euclid's algorithm for finding the HCF
  // of two integers A and B.
  if (a < b)
  {
    hcf = a;
    i = b;
  }
  else
  {
    hcf = b;
    i = a;
  }
  do
  {
    j = i % hcf;
    if (j != 0)
    {
      i = hcf;
      hcf = j;
    }
  } while (j != 0);
  return hcf;
}

void SpinWheels(TCanvas * TargetCanvas,
                int a, int b, int d,
                int CenterX, int CenterY,
                TColor DrawColor)
{
  int rab, Lines, i;
  float Alpha, Beta, ADif, AoverB;
  float xpt, ypt;  // line endpoint coordinates

  rab = a-b;
  Alpha = 0.0;

  // The constants 100 and 200 here control the smoothness of 
  // the displayed or printed curve. The larger value must be 
  // twice the value that divides PI.  Larger values make for 
  // smoother curves but longer draw times since there are    
  // more line segments in the drawn curve.
  ADif = M_PI/100.0;
  AoverB = ((float)a)/b;
  Lines = 200 * (b/HighestCommonFactor(a,b));
  TargetCanvas->Pen->Color = DrawColor;
  TargetCanvas->MoveTo((rab+d+CenterX+0.5),CenterY);
  for (i = 1; i <= Lines; i++)
  {
    Alpha = Alpha + ADif;
    Beta = Alpha * AoverB;
    xpt = rab*cos (Alpha) + d*cos(Beta);
    ypt = rab*sin (Alpha) - d*sin(Beta);
    TargetCanvas->LineTo(xpt+CenterX+0.5, ypt+CenterY+0.5);
  }
}

SPINFORM.H

//---------------------------------------------------------------------------
#ifndef spinformH
#define spinformH
//---------------------------------------------------------------------------
#include <vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
#include <vcl\ExtCtrls.hpp>
#include <vcl\Dialogs.hpp>
#include "sampreg.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components 
        TPanel *ButtonPanel;
        TButton *SpinButton;
        TButton *PrintButton;
        TButton *ClearButton;
        TButton *PickPrinterButton;
        TButton *QuitButton;
        TLabel *Label1;
        TLabel *Label2;
        TLabel *Label3;
        TPrinterSetupDialog *PrinterSetupDialog1;
        TSpinEdit *SpinA;
        TSpinEdit *SpinB;
        TSpinEdit *SpinD;
        TColorGrid *ColorGrid1;
        void __fastcall QuitButtonClick(TObject *Sender);
        void __fastcall SpinButtonClick(TObject *Sender);
        void __fastcall ClearButtonClick(TObject *Sender);
        void __fastcall PrintButtonClick(TObject *Sender);
        void __fastcall PickPrinterButtonClick(TObject *Sender);
        void __fastcall FormCreate(TObject *Sender);
        void __fastcall FormPaint(TObject *Sender);
private:	// User declarations
public:		// User declarations
        virtual __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

SPINNER.CPP


//---------------------------------------------------------------------------
//                   SpiroMania
//          SPINFORM.PAS : Main Form Unit
//            By Jeff Duntemann KG7JF
//
// The main form unit for a cycloid curve generator
// functionally similar to the Kenner Spirograph toy.
// Written for *The New Delphi 2 Programming Explorer*
// Converted to C++ for *The C++Builder Programming Explorer*
// Copyright (c) 1996,1997 The Coriolis Group, Inc.
//              Last Updated 1/13/97
//---------------------------------------------------------------------------
#include 
#pragma hdrstop

#include 
#include "spinform.h"
#include "spinner.h"
//---------------------------------------------------------------------------
#pragma link "sampreg"
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
bool CanvasIsClear;  // Flags when pattern needs to be regenned
                     // from within the Paint method
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::QuitButtonClick(TObject *Sender)
{
  Close ();        
}
//---------------------------------------------------------------------
void __fastcall TForm1::SpinButtonClick(TObject *Sender)
{
  int CenterX, CenterY;

  CanvasIsClear = false;
  CenterY = ClientHeight / 2;
  CenterX = (ClientWidth + ButtonPanel->Width)/2;
  Canvas->Pixels[CenterX][CenterY] = clBlack;
  SpinWheels (Canvas, SpinA->Value, SpinB->Value, SpinD->Value,
              CenterX, CenterY, ColorGrid1->ForegroundColor);
}
//---------------------------------------------------------------------
void __fastcall TForm1::ClearButtonClick(TObject *Sender)
{
  CanvasIsClear = true;  // Canvas is clear; don't repaint pattern on Paint
  Refresh ();
}
//---------------------------------------------------------------------
/*
  Printing may seem complex, but it's really only a matter of using
  a scaling factor with SpinWheels and passing the printer canvas
  to SpinWheels as a parameter.  SpinWheels is executed between the
  BeginDoc/EndDoc pair...and that's it!
*/
void __fastcall TForm1::PrintButtonClick(TObject *Sender)
{
  int CenterX, CenterY;
  int ScaleFactor;
  String SpinAText, SpinBText, SpinDText, LabelText;
  TPrinter * Printer;

  Printer = Printers::Printer();
  /*
    Calculate a scale factor, which is the ratio of the shortest
    dimension of the printed page to the shortest dimension of
    the displayed form canvas
  */
  ScaleFactor = Printer->PageWidth/ClientHeight;
  CenterX = Printer->PageWidth/2;
  CenterY = Printer->PageHeight/2;
  try
  {
    Printer->BeginDoc ();
    /*
      We spin the wheels again on the printer canvas, generating
      the same pattern shown on the form, only scaled to fill the
      same portion of the printer canvas.
    */
    SpinWheels(Printer->Canvas,
               SpinA->Value*ScaleFactor,
               SpinB->Value*ScaleFactor,
               SpinD->Value*ScaleFactor,
               CenterX, CenterY, clBlack);
    // Create the label for the printed sheet.
    LabelText = "A=" + IntToStr(SpinA->Value)+
              "   B="+IntToStr(SpinB->Value)+
              "   D="+IntToStr(SpinD->Value);
    // We're re-using CenterX and CenterY here to position the label
    // that displays the spin parameters on the printed shee.
    CenterY = (Printer->PageHeight - (Printer->PageHeight * 0.07));
    CenterX = (Printer->PageWidth - Canvas->TextWidth(LabelText)) / 2;
    Canvas->TextOut (CenterX, CenterY, LabelText);
  }
  catch (...)
  {
  }
  if (Printer->Printing)
    Printer->EndDoc ();
}
//---------------------------------------------------------------------
// This is all it takes to run the Printer Setup dialog box.
// It's really all done by Windows.                                                   
void __fastcall TForm1::PickPrinterButtonClick(TObject *Sender)
{
  PrinterSetupDialog1->Execute ();
}
//---------------------------------------------------------------------
// We only need to set the default values once for the cycloid pattern,
// so this is done when the form is created.  We could have just as
// easily set them with the Object Inspector.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  SpinA->Value = 200;
  SpinB->Value = 70;
  SpinD->Value = 65;
}
//---------------------------------------------------------------------
// All the real action happens when the form is redrawn.  Each time that
// happens, the pattern is regenerated using SpinWheels.
void __fastcall TForm1::FormPaint(TObject *Sender)
{
  int CenterX, CenterY;

  if (!CanvasIsClear)
  {
    CenterY = ClientHeight / 2;
    CenterX = (ClientWidth + ButtonPanel->Width)/2;
    Canvas->Pixels[CenterX][CenterY] = clBlack;
    SpinWheels (Canvas, SpinA->Value, SpinB->Value, SpinD->Value,
                CenterX, CenterY, ColorGrid1->ForegroundColor);
  }
}
//---------------------------------------------------------------------




Printing Text

Canvas->TextOut( X, Y, SomeText);

MyCanvas->Font->Name = "Courier New";

Style, Color, Size

DRAWING AND REDRAWING



All text and graphics must be redrawn whenever a form is covered/uncovered or minimized/maximized

Use the OnPaint event FormPaint



CS350 Week 11/12

Chapter 14 - Tables on the Record


bits -> bytes(characters) -> fields -> records -> files -> databases

  1. Data Integration- integration of IS;
    • DBMS; integrated data base; database vs. data base
  2. Benefits of a Data Base Environment
    1. Greater Access To Info
    2. Better Control - Data Structures
    3. More Efficient Systems Development - program-independent
  3. Approaches to Data Base Management
    1. hierarchical DBMS: Information Management System (IMS)
      • occurence of data base structure; segment; data
      • base record; parent-child relationship; root
    2. Network or CODASYL DBMS
      A Network DBMS Example: 1-many; set; schema
    3. Relational DBMS
      1. A Relational DBMS Example: Table; tuple;attribute
      2. Queries to the DBMS
      3. DB2, ORACLE, FOCUS, IDMS/R, Ingres
      4. Standards for DBMSs SQL(Structured Query Language)
    4. Object-oriented DBMS
      uses objects and messages to accommodate types of data and provide for data handling

DATABASE DESIGN CONSIDERATIONS

logical database design - way user views data
physical database design - actual structure of database
data dictionary
schema- complete description of content and structure of database
subschema - each user's view of the database

Different Database Formats

Microsoft's ODBC (Open Database Connectivity) - Access
Borland's IDAPI - Paradox, dBase

DATABASE DESKTOP


DBD - can load, edit & restructure tables created for use with C++ Builder
cannot print, output reporting, edit memo fields or BLOBs (Binary Large Objects)

PROJECT 1: Use DBD to look at the AUTHORS.DB table; check out the structure of the table
Create a copy of AUTHORS.DB called PRACTICE.DB and save it on your disk. Make sure to copy and rename all files that begin with AUTHORS.

Use DBD to create a new table (Figs. 14.4 & 14.5)

FIELD NAMES

  • Use all caps for Field Names
  • End key field names with ID
  • Use Table Name in field names unique to table

Key Fields

  • must be unique; used for searching and relational linkage
  • for Paradox, all key fields must be the first fields listed in the table

SECONDARY INDEXES

  • secondary used in a lookup table (ex. LAST NAME field in AUTHORS.DB)
  • do not have to be unique
  • needed in dBase and Paradox for searching a table

PROJECT 2: Create a secondary index of AUTHOR CALL for PRACTICE.DB.

C++ Builder'S DATABASE MACHINERY

Data access components - Table, DataSource, Query...
Data aware components - DBEdit, DBText, DBMemo, DBGrid, ...

Table Component - logical representation of the database table
DataSource Component - interface between Table component and data aware components (contains event that records when a record has changed in the Table)
TFieldList Component - "list" of Field Objects in a Table component

Setting Up for Data Access

  1. Set Database property (directory) for the Table (DOS path name / alias),
  2. Assign the TableName property and
  3. Set the DataSet property for the DataSource

PROGRAMMATIC DATABASE ACCESS

Use DBNavigator to browse through a Table

Accessing Fields:


AuthorTable->Fields[0]->DisplayLabel = 'ID'; (uses offset)
AuthorTable->FieldByName('Author ID')->DisplayLabel = 'ID'; (uses name of field's column in the database table)

Inspecting Fields:


double-click the Table Component brings up a Field Editor and puts a Field Object into the Object Inspector
PROJECT 3: Create a project SEEKTEST.DPR with a form that allows searching the AUTHORS.DB table by name or key.

Write a OnCreate event handler to set up the fields in the DBGrid component

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  /*
    You can access fields by their index into the fields list.
    That's what I'm doing here, just to demonstrate it:
  */
  AuthorTable->Fields[0]->DisplayLabel = "ID";
  // In most cases, it makes more sense to access fields by name:
  AuthorTable->FieldByName("AUTHOR ID")->DisplayWidth = 4;
  AuthorTable->FieldByName("AUTHOR ID")->ReadOnly = True;
  AuthorTable->FieldByName("AUTHOR LAST NAME")->DisplayLabel = "LAST NAME";
  AuthorTable->FieldByName("AUTHOR LAST NAME")->DisplayWidth = 12;
  AuthorTable->FieldByName("AUTHOR FIRST NAME")->DisplayLabel = "FIRST NAME";
  AuthorTable->FieldByName("AUTHOR FIRST NAME")->DisplayWidth = 10;
  AuthorTable->FieldByName("AUTHOR CALL")->DisplayLabel = "CALL";
  AuthorTable->FieldByName("AUTHOR CALL")->DisplayWidth = 10;
  AuthorTable->FieldByName("BIRTH YEAR")->Visible = False;
  AuthorTable->FieldByName("DEATH YEAR")->Visible = False;
}

SEARCHING A TABLE

(make sure that the field used in the search is a key field or a secondary index)
GOTO METHOD
  1. Make sure field is a key field or secondary index & is present in TTable's property list!!
  2. Place Table being searched into search mode by calling SetKey method
  3. Assign value of search field into Field's search buffer.
  4. Call the Table's GotoKey method and test it's return value.

void __fastcall TForm1::SeekButtonClick(TObject *Sender)
{
  CallLabel->Caption = "";
  AuthorTable->IndexName = "LAST NAME INDEX";
  AuthorTable->SetKey ();
  AuthorTable->FieldByName("AUTHOR LAST NAME")->AsString = EntryEdit->Text;
  if (AuthorTable->GotoKey ())
  {
    StatusLabel->Caption = "Entry Found!";
    CallLabel->Caption = AuthorTable->FieldByName("AUTHOR CALL")->Text;
  }
  else
  {
    StatusLabel->Caption = "Entry Not Found!";
  }
}

Searching on a Key Field

void __fastcall TForm1::KeySearchButtonClick(TObject *Sender)
{
  CallLabel->Caption = "";
  AuthorTable->IndexFieldNames = "AUTHOR ID";
  AuthorTable->SetKey ();
  AuthorTable->FieldByName ("AUTHOR ID")->AsString = EntryEdit->Text;
  if (AuthorTable->GotoKey ())
  {
    StatusLabel->Caption = "Entry Found!";
    CallLabel->Caption = AuthorTable->FieldByName ("AUTHOR CALL")->Text;
  }
  else
  {
    StatusLabel->Caption = "Entry Not Found!";
  }
}

IndexName is for named secondary indexes
IndexFieldNames is for names of fields

Using Find for Searches

void __fastcall TForm1::FindCallButtonClick(TObject *Sender)
{
  String SeekValue;

  AuthorTable->IndexFieldNames = "AUTHOR CALL";
  SeekValue = EntryEdit->Text;
  if (AuthorTable->FindKey (OPENARRAY (TVarRec, (SeekValue))))
  {
    StatusLabel->Caption = "Entry Found";
  }
  else
  {
    StatusLabel->Caption = "Entry Not Found";
  }
}

Allows for multiple index searches by including all indexes in the IndexFieldNames and including them as parameters in the FindKey function.

InExact Searches with GotoNearest and FindNearest

void __fastcall TForm1::FindNearestButtonClick(TObject *Sender)
{
  AuthorTable->IndexName = "LAST NAME INDEX";
  AuthorTable->SetKey ();
  AuthorTable->FieldByName ("AUTHOR LAST NAME")->AsString = EntryEdit->Text;
  AuthorTable->GotoNearest ();
  CallLabel->Caption = AuthorTable->FieldByName ("AUTHOR CALL")->Text;
}

CHANGING DATA IN EXISTING RECORDS

  1. Move current pointer to record you want to change
  2. Place Table in Edit mode by calling its Edit method
  3. Change the Fields that need to be changed
  4. Write the changes to the Table by calling its Post method
ex. CASETEST.MAK

	SomeTable->DisableControls(); 	// turn off display of data during update
	SomeTable->First();			// start at top of table
	while (!SomeTable->Eof)
	{
		{read a value from a Field into a temporary variable}
		{make some change to the variable}
		SomeTable->Edit;		{Put Table in Edit mode}
		{Write the changed value back into the field}
		SomeTable->Post();		{Write changes to Table}
		SomeTable->Next();		{Move down to next record}
	}
	SomeTable->EnableControls();	{Turn display of data back on again}

TRY..FINALLY for Exception Protection

Try block of statements contain code subject to exceptions
Finally block of statements must be executed even if there is an exception

try
{
	SomeTable->DisableControls(); 	{ turn off display of data during update}
	SomeTable->First();			{ start at top of table}
	while (!SomeTable->Eof)
	{
			{read a value from a Field into a temporary variable}
			{make some change to the variable}
			SomeTable->Edit();		{Put Table in Edit mode}
			{Write the changed value back into the field}
			SomeTable->Post();		{Write changes to Table}
			SomeTable->Next();		{Move down to next record}
	}
}
catch (...)
{
	SomeTable->EnableControls();	{Turn display of data back on again}
}
SomeTable->EnableControls();	{Turn display of data back on again}

//---------------------------------------------------------------------------
#include 
#pragma hdrstop

#include "caseform.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TEditDemoForm *EditDemoForm;
//---------------------------------------------------------------------------
__fastcall TEditDemoForm::TEditDemoForm(TComponent* Owner)
        : TForm(Owner)
{
}

static const bool Upper = true;
static const bool Mixed = false;

void TEditDemoForm::ForceCase (String TargetField, bool ToUpper)
{
  String WorkBuffer;
  int i;

  AuthorTable->DisableControls ();
  try
  {
    AuthorTable->First ();        // Start at the top of the table
    while (!AuthorTable->Eof)  // do until end of table
    {
      WorkBuffer = AuthorTable->FieldByName (TargetField)->AsString;
      if (ToUpper)
      {
        for (i = 0; i < WorkBuffer.Length (); i++)
        {
          WorkBuffer[i] = (char)toupper (WorkBuffer[i]);
        }
      }
      else
      {
        WorkBuffer[0] = (char)toupper (WorkBuffer[0]);
        for (i = 1; i < WorkBuffer.Length (); i++)
        {
          WorkBuffer[i] = (char)tolower (WorkBuffer[i]);
        }
      }
      // This is the only place we actually write to the table
      AuthorTable->Edit ();  // put table in edit mode
      AuthorTable->FieldByName (TargetField)->AsString = WorkBuffer;
      AuthorTable->Post ();  // write changes to table
      AuthorTable->Next ();  // bump to next record down
    }
  }
  catch (...)
  {
    AuthorTable->EnableControls ();
  }
  AuthorTable->EnableControls ();
}
//---------------------------------------------------------------------
void __fastcall TEditDemoForm::FormCreate(TObject *Sender)
{
  /*
    You can access fields by their index into the fields list.
    That's what I'm doing here, just to demonstrate it:
  */
  AuthorTable->Fields[0]->DisplayLabel = "ID";
  // In most cases, it makes more sense to access fields by name:
  AuthorTable->FieldByName("AUTHOR ID")->DisplayWidth = 4;
  AuthorTable->FieldByName("AUTHOR ID")->ReadOnly = True;
  AuthorTable->FieldByName("AUTHOR LAST NAME")->DisplayLabel = "LAST NAME";
  AuthorTable->FieldByName("AUTHOR LAST NAME")->DisplayWidth = 12;
  AuthorTable->FieldByName("AUTHOR FIRST NAME")->DisplayLabel = "FIRST NAME";
  AuthorTable->FieldByName("AUTHOR FIRST NAME")->DisplayWidth = 10;
  AuthorTable->FieldByName("AUTHOR CALL")->DisplayLabel = "CALL";
  AuthorTable->FieldByName("AUTHOR CALL")->DisplayWidth = 10;
  AuthorTable->FieldByName("BIRTH YEAR")->Visible = False;
  AuthorTable->FieldByName("DEATH YEAR")->Visible = False;
}
//---------------------------------------------------------------------

void __fastcall TEditDemoForm::UpperCaseFirstNameButtonClick(TObject *Sender)
{
  ForceCase("AUTHOR FIRST NAME", Upper);
}
//---------------------------------------------------------------------
void __fastcall TEditDemoForm::UpperCaseLastNameButtonClick(TObject *Sender)
{
  ForceCase("AUTHOR LAST NAME",Upper);
}
//---------------------------------------------------------------------
void __fastcall TEditDemoForm::MixedCaseLastNameButtonClick(TObject *Sender)
{
  ForceCase("AUTHOR LAST NAME",Mixed);        
}
//---------------------------------------------------------------------
void __fastcall TEditDemoForm::MixedCaseFirstNameButtonClick(TObject *Sender)
{
  ForceCase("AUTHOR FIRST NAME",Mixed);        
}
//---------------------------------------------------------------------
void __fastcall TEditDemoForm::QuitButtonClick(TObject *Sender)
{
  Close ();
}
//---------------------------------------------------------------------

INSERTING AND DELETING RECORDS

To delete a record, position the pointer at the record to be deleted and call the Table's Delete method. [Good habit to include a confirmation message box for the user]

To insert a record, call the Table's Insert or Append method.

Piecemeal Insertion


	DeviceTable->Insert();
	DeviceTable->FieldByName('DEVICE NAME')->AsString = "12AD6";
	DeviceTable->FieldByName('DEVICE DESCRIPTION')->AsString =
		"12V Space charge pentagrid converter";
	DeviceTable->Post();

Inserting a Record with a Single Statement


InsertRecord(OPENARRAY(TVarRec, (NULL, NULL, "12V Space charge pentagrid converter")));
//---------------------------------------------------------------------------
#include 
#pragma hdrstop

#include "addform.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TInsertDeleteForm *InsertDeleteForm;
//---------------------------------------------------------------------------
__fastcall TInsertDeleteForm::TInsertDeleteForm(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TInsertDeleteForm::QuitButtonClick(TObject *Sender)
{
  Close ();        
}
//---------------------------------------------------------------------
void __fastcall TInsertDeleteForm::FormCreate(TObject *Sender)
{
  DeviceTable->Fields[0]->Visible = False;
  DeviceTable->FieldByName("DEVICE NAME")->DisplayLabel = "NAME";
  DeviceTable->FieldByName("DEVICE NAME")->DisplayWidth = 10;
  DeviceTable->FieldByName("DEVICE DESCRIPTION")->DisplayLabel = "DESCRIPTION";
}
//---------------------------------------------------------------------
void __fastcall TInsertDeleteForm::CreateNewRecordButtonClick(
        TObject *Sender)
{
  if ((DeviceNameEdit->Text.Length () == 0) &&
      (DeviceDescriptionEdit->Text.Length () == 0))
  {
    ShowMessage ("No data has been entered!");
  }
  else
  {
    DeviceTable->IndexFieldNames = "DEVICE NAME";
    if (DeviceTable->FindKey(OPENARRAY (TVarRec, (DeviceNameEdit->Text))))
    {
      ShowMessage ("That device already exists!");
    }
    else
    {
      DeviceTable->InsertRecord(OPENARRAY (TVarRec, (
        NULL, DeviceNameEdit->Text,DeviceDescriptionEdit->Text)));
    }
    DeviceNameEdit->Text = "";
    DeviceDescriptionEdit->Text = "";
  }
}
//---------------------------------------------------------------------
void __fastcall TInsertDeleteForm::DeleteButtonClick(TObject *Sender)
{
  if ((DeviceNameEdit->Text.Length () == 0) &&
      (DeviceDescriptionEdit->Text.Length () == 0))
  {
    ShowMessage ("No data has been entered!");
  }
  else
  {
    // We first have to make sure the entered device is in the table
    DeviceTable->IndexFieldNames = "DEVICE NAME";
    if (DeviceTable->FindKey(OPENARRAY (TVarRec, (DeviceNameEdit->Text))))
    {    // If it is, we must confirm the deletion
      Set tempSet;
      tempSet << mbYes << mbNo;
      if (MessageDlg("Are you sure you want to delete this record?",
                mtConfirmation, tempSet, 0) == mrYes)
      {
        DeviceTable->Delete ();  // Deletes the current record
      }
      else              // If it's not found, display an error box
      {
        ShowMessage ("That device is not in the table!");
      }
    }
    // Finally, clear the edit boxes
    DeviceNameEdit->Text = "";
    DeviceDescriptionEdit->Text = "";
  }
}
//---------------------------------------------------------------------

WHAT'S LEFT


Queries, SQL, Interbase, bookmarks, ODBC



CS350 Week 13/14

Chapter 16 - SQL As API


Application Programming Interface

Application Programming Interface (API) - group of function calls (typically into Window's code libraries called Dynamic Link Libraries) that serve a common purpose

Dividing the Controller From the Controlled

client/server - separates the database from the applications
BDE (Borland Database Engine) - suite of DLLs that supports "local" database applications

SQL FROM A HEIGHT

SQL command - typically a query:
  SELECT LAST_NAME FROM CUST_TABLE WHERE LAST_ORDER_DATE > 1/1/97
  DROP TABLE "TEMPDATA.DB"
SQL queries usually result in result set

SQL and the TQuery Component

Local SQL is handled through a TQEURY Component
requires a TDataSource Component
TQUERY returns a result set which typically includes a subset of records and often a subset of fields

The SQL Property

SQL Property of TQuery is of type TStrings and is used to hold the SQL query
	UPDATE SEMINAME
	
	SET DEV_TYPE = :dType,
		DEV_PKG = :dPkg,
		DEV_DESCRIP = :dDescrip,
		STOCK_LEVEL = :dLevel,
		SORT_STRING = :dSort
		
	WHERE DEV_NAME = dName

You can load a different query into the SQL property dynamically either with the Add method or the LoadFromFile method:
Query1->SQL->Add("SELECT * FROM SEMINAME");
Query1->SQL->LoadFromFile("INSERT.DOC");

ex. SEMICONS.MAK

/*
        SQL Demonstration Application
          By Jeff Duntemann KG7JF

   FOR: The New Delphi Programming Explorer
   (C) 1996 by The Coriolis group, Inc.
   Converted to C++ for *The C++Builder Programming Explorer*
   Last Modified 1/20/97
*/
#include <vcl\vcl.h>
#pragma hdrstop

#include "semiform.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
int MaxRecordNum;

String DeriveSortString (String DeviceName)
{
  int i, ResultIndex;
  String Result;

  /*
    We work from the end of the string forward, copying numeric
    characters to the sort string until we encounter an alpha.
    First, fill the function's result variable with zeroes:
  */
  Result = "000000000000";
  ResultIndex = Result.Length () - 1;
  if (DeviceName.Length () < 1)
  {
    return Result;            // Quit on null name
  }
  // Result now contains 12 characters of '0'
  // Next, trim non-numerics from trailing end of name:
  i = DeviceName.Length () - 1;
  while (!isdigit (DeviceName[i]))
  {
    DeviceName.Delete (i, 1);
    i--;
  }

  // Now, copy numeric chars to Result as they're encountered:
  for (i = DeviceName.Length() - 1; i >= 0; i--)
  {
    if (isdigit (DeviceName[i]))
    {
      Result[ResultIndex] = DeviceName[i];
      ResultIndex--;
    }
    else
    {
      break;  // first alpha we find ends the loop
    }
  }
  return Result;
}


void __fastcall TForm1::ClearAllEntryFields (void)
{
  NameField->Text = "";
  PackageField->Text = "";
  StockField->Text = "";
  TypesGroup->ItemIndex = 0;
  DescriptionField->Text = "";
  SortStringField->Text = "";
}


void __fastcall TForm1::QuitButtonClick(TObject *Sender)
{
  Close ();        
}
//---------------------------------------------------------------------
void __fastcall TForm1::AddButtonClick(TObject *Sender)
{
  if (NameField->Text.Length () == 0)
  {
    ShowMessage ("You haven't entered a device name!");
  }
  else
  {
    SemiQuery1->Close ();
    SemiQuery1->SQL->Clear ();
    SemiQuery1->SQL->LoadFromFile("INSERT.SQL");
    SemiQuery1->Prepare ();
    SemiQuery1->ParamByName("dID")->AsInteger = MaxRecordNum + 1;
    SemiQuery1->ParamByName("dName")->AsString = NameField->Text;
    SemiQuery1->ParamByName("dType")->AsString =
      TypesGroup->Items->Strings[TypesGroup->ItemIndex];
    SemiQuery1->ParamByName("dPkg")->AsString = PackageField->Text;
    SemiQuery1->ParamByName("dDescrip")->AsString = DescriptionField->Text;
    SemiQuery1->ParamByName("dLevel")->AsInteger = StrToInt(StockField->Text);
    SemiQuery1->ParamByName("dSort")->AsString = SortStringField->Text;
    SemiQuery1->ExecSQL ();
    SemiQuery1->Close ();
    MaxRecordNum = MaxRecordNum + 1;
    MaxRecLabel->Caption = IntToStr(MaxRecordNum);
    ClearAllEntryFields ();
    UniqueLabel->Caption = "";
    DupeLabel->Caption = "";
  }
}
//---------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  // Always begin execution on Add Device page:
  TabbedNotebook1->PageIndex = 0;
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->Add("SELECT MAX(DEV_ID) FROM SEMINAME");
  SemiQuery1->Open ();
  MaxRecordNum = SemiQuery1->Fields[0]->AsInteger;
  MaxRecLabel->Caption = SemiQuery1->Fields[0]->AsString;
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->Add("SELECT * FROM SEMINAME");
  SemiQuery1->Open ();
  ClearAllEntryFields ();
}
//---------------------------------------------------------------------
void __fastcall TForm1::CloneButtonClick(TObject *Sender)
{
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  // The result set is the entire table:
  SemiQuery1->SQL->Add("SELECT * FROM SEMINAME");
  SemiQuery1->Open ();  // Make the query and thus create the result set
  // We can now navigate the result set as though it were a TTable:
  SemiQuery1->Last ();
  // Data is retrieved from the fields just as with TTable:
  NameField->Text = SemiQuery1->FieldByName("DEV_NAME")->AsString;
  PackageField->Text = SemiQuery1->FieldByName("DEV_PKG")->AsString;
  StockField->Text = SemiQuery1->FieldByName("STOCK_LEVEL")->AsString;
  DescriptionField->Text = SemiQuery1->FieldByName("DEV_DESCRIP")->AsString;
  SemiQuery1->Close ();
}
//---------------------------------------------------------------------
void __fastcall TForm1::ClearButtonClick(TObject *Sender)
{
  ClearAllEntryFields ();
}
//---------------------------------------------------------------------
void __fastcall TForm1::OpenAllInOrder (void)
{
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->Add("SELECT * FROM SEMINAME ORDER BY SORT_STRING ASC");
  SemiQuery1->Open ();
  SemiQuery1->FieldByName("DEV_ID")->Visible = False;
  SemiQuery1->FieldByName("SORT_STRING")->Visible = False;
  SemiQuery1->FieldByName("DEV_NAME")->DisplayLabel = "Name";
  SemiQuery1->FieldByName("DEV_NAME")->DisplayWidth = 8;
  SemiQuery1->FieldByName("DEV_TYPE")->DisplayLabel = "Type";
  SemiQuery1->FieldByName("DEV_PKG")->DisplayLabel = "Package";
  SemiQuery1->FieldByName("DEV_DESCRIP")->DisplayLabel = "Description";
  SemiQuery1->FieldByName("DEV_DESCRIP")->DisplayWidth = 33;
  SemiQuery1->FieldByName("STOCK_LEVEL")->DisplayLabel = "Stock";
  SemiQuery1->FieldByName("STOCK_LEVEL")->DisplayWidth = 5;
}
//---------------------------------------------------------------------
void __fastcall TForm1::TabbedNotebook1Change(TObject *Sender, int NewTab,
        bool &AllowChange)
{
  switch (NewTab)
  {
    case 2 : // Drop Device Page
      OpenAllInOrder ();  // Open the full table in SORT_STRING order
      SemiQuery1->FieldByName("DEV_TYPE")->Visible = false;
      SemiQuery1->FieldByName("DEV_PKG")->Visible = false;
      // This simply displays the name of the item currently
      // at the query cursor, in the DeleteLookupField:
      DeleteLookupLabel->Caption =
        SemiQuery1->FieldByName("DEV_NAME")->AsString;
      break;

    case 3 :  // View By Name Page
      OpenAllInOrder (); // Open the full table in SORT_STRING order
      break;

    case 4 :  // View by Types Page
      ShowByTypesGroup->ItemIndex = -1;
      ShowByTypesCountLabel->Caption = "";
      OpenAllInOrder ();
      SemiQuery1->FieldByName("DEV_TYPE")->Visible = false;
      SemiQuery1->FieldByName("DEV_ID")->Visible = false;
      SemiQuery1->FieldByName("DEV_PKG")->Visible = false;
      SemiQuery1->FieldByName("SORT_STRING")->Visible = false;
      SemiQuery1->FieldByName("DEV_NAME")->DisplayWidth = 9;
      SemiQuery1->FieldByName("DEV_DESCRIP")->DisplayWidth = 40;
      SemiQuery1->FieldByName("STOCK_LEVEL")->DisplayWidth = 5;
      ShowByTypesCountCaptionLabel->Caption =
        "All devices in the table are shown->";
      break;
  }
}
//---------------------------------------------------------------------
void __fastcall TForm1::NameFieldExit(TObject *Sender)
{
  SortStringField->Text = DeriveSortString(NameField->Text);
}
void __fastcall TForm1::SemiSourceDataChange(TObject *Sender,
        TField *Field)
{
  if (TabbedNotebook1->PageIndex == 2)
  {
    DeleteLookupLabel->Caption =
      SemiQuery1->FieldByName("DEV_NAME")->AsString;
  }
}
//---------------------------------------------------------------------
void __fastcall TForm1::DeleteButtonClick(TObject *Sender)
{
  String DeviceToDelete;
  bool AllowChangeValue;
  /*
    When the user clicks the Delete button, we save the name
    of the device at the current query cursor, then confirm:
  */
  DeviceToDelete = SemiQuery1->FieldByName("DEV_NAME")->AsString;
  if (DeviceToDelete.Length () == 0)
  {
    ShowMessage ("No device selected");
  }
  else
  {
    Set<TMsgDlgBtn, 0, 8> tempSet;
    tempSet << mbYesNoCancel;

    if (MessageDlg ("Delete "+DeviceToDelete+"?",
            mtConfirmation, tempSet, 0) == mrYes)
    {
      SemiQuery1->Close ();
      SemiQuery1->SQL->Clear ();
      SemiQuery1->SQL->Add ("DELETE FROM SEMINAME WHERE DEV_NAME = :dName");
      SemiQuery1->Prepare ();
      // Here we plug in DeviceToDelete for ":dName":
      SemiQuery1->ParamByName("dName")->AsString = DeviceToDelete;
      SemiQuery1->ExecSQL ();  // Do the DELETE Query!
      SemiQuery1->Close ();    // This task is done; close the TQuery
    }
  }
  DeleteLookupField-> Text = "";
  /*
    This is a sneaky way to re-use the code that opens the query
    on the full table, by triggering the event that normally
    happens only when you change the tabbed notebook page
  */
  TabbedNotebook1Change (DeleteButton,2,AllowChangeValue);
}
//---------------------------------------------------------------------
void __fastcall TForm1::DeleteLookupFieldChange(TObject *Sender)
{
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->Add("SELECT * FROM SEMINAME WHERE DEV_NAME LIKE :dName");
  SemiQuery1->Prepare ();
  SemiQuery1->ParamByName("dName")->AsString = DeleteLookupField->Text+"%";
  SemiQuery1->Open ();
}


void __fastcall TForm1::ChangeLookupFieldChange(TObject *Sender)
{
  String TypeString;

  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->Add("SELECT * FROM SEMINAME WHERE DEV_NAME LIKE :dName");
  SemiQuery1->Prepare ();
  SemiQuery1->ParamByName("dName")->AsString = ChangeLookupField->Text + "%";
  SemiQuery1->Open ();
  // Display number of records in result set
  MatchCountLabel->Caption = IntToStr(SemiQuery1->RecordCount);
  SemiQuery1->First ();  // Make sure you"re on first record in result set
  // Put the existing record values into the change entry fields
  ChangeNameLabel->Caption     = SemiQuery1->FieldByName("DEV_NAME")->AsString;
  ChangeSortStringField->Text  = SemiQuery1->FieldByName("SORT_STRING")->AsString;
  ChangePackageField->Text     = SemiQuery1->FieldByName("DEV_PKG")->AsString;
  ChangeStockField->Text       = SemiQuery1->FieldByName("STOCK_LEVEL")->AsString;
  ChangeDescriptionField->Text = SemiQuery1->FieldByName("DEV_DESCRIP")->AsString;
  TypeString                   = SemiQuery1->FieldByName("DEV_TYPE")->AsString;
  ChangeTypesGroup->ItemIndex  =
    ChangeTypesGroup->Items->IndexOf(TypeString);
}
//---------------------------------------------------------------------
void __fastcall TForm1::ClearChangesButtonClick(TObject *Sender)
{
  if (SemiQuery1->Active)
  {    // Only if a result set is open!
    ChangeSortStringField->Text = SemiQuery1->FieldByName("SORT_STRING")->AsString;
    ChangePackageField->Text = SemiQuery1->FieldByName("DEV_PKG")->AsString;
    ChangeStockField->Text = SemiQuery1->FieldByName("STOCK_LEVEL")->AsString;
    ChangeDescriptionField->Text = SemiQuery1->FieldByName("dev_descrip")->AsString;
  }
}
//---------------------------------------------------------------------
void __fastcall TForm1::SaveChangesButtonClick(TObject *Sender)
{
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->LoadFromFile("UPDATE.SQL");
  SemiQuery1->Prepare ();
  SemiQuery1->ParamByName("dName")->AsString = ChangeNameLabel->Caption;
  if (ChangeTypesGroup->ItemIndex >= 0)
    SemiQuery1->ParamByName("dType")->AsString =
      ChangeTypesGroup->Items->Strings[ChangeTypesGroup->ItemIndex];
  else
    SemiQuery1->ParamByName("dType")->AsString = "";
  SemiQuery1->ParamByName("dPkg")->AsString = ChangePackageField->Text;
  SemiQuery1->ParamByName("dDescrip")->AsString = ChangeDescriptionField->Text;
  SemiQuery1->ParamByName("dLevel")->AsInteger = StrToInt(ChangeStockField->Text);
  SemiQuery1->ParamByName("dSort")->AsString = ChangeSortStringField->Text;
  SemiQuery1->ExecSQL ();  // Used for queries that don"t return result sets!
  SemiQuery1->Close ();
  // Clear out all values from the change edit fields:
  ChangeNameLabel->Caption = "";
  ChangeSortStringField->Text = "";
  ChangePackageField->Text = "";
  ChangeStockField->Text = "";
  ChangeDescriptionField->Text = "";
  ChangeTypesGroup->ItemIndex = -1;
}
//---------------------------------------------------------------------
void __fastcall TForm1::NameFieldChange(TObject *Sender)
{
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->Add("SELECT * FROM SEMINAME WHERE DEV_NAME LIKE :dName");
  SemiQuery1->Prepare ();
  SemiQuery1->ParamByName("dName")->AsString = NameField->Text + "%";
  SemiQuery1->Open ();
  if (SemiQuery1->RecordCount > 0)
  {
    DupeLabel->Caption = "Dupe!";
    UniqueLabel->Caption = "";
  }
  else
  {
    DupeLabel->Caption = "";
    UniqueLabel->Caption = "Unique!";
  }
}
//---------------------------------------------------------------------
void __fastcall TForm1::ShowByTypesGroupClick(TObject *Sender)
{
  SemiQuery1->Close ();
  SemiQuery1->SQL->Clear ();
  SemiQuery1->SQL->Add("SELECT * FROM SEMINAME WHERE DEV_TYPE LIKE :dType");
  SemiQuery1->Prepare ();
  SemiQuery1->ParamByName("dType")->AsString =
    ShowByTypesGroup->Items->Strings[ShowByTypesGroup->ItemIndex];
  SemiQuery1->Open ();
  SemiQuery1->FieldByName("DEV_TYPE")->Visible = False;
  SemiQuery1->FieldByName("DEV_ID")->Visible = False;
  SemiQuery1->FieldByName("DEV_PKG")->Visible = False;
  SemiQuery1->FieldByName("SORT_STRING")->Visible = False;
  SemiQuery1->FieldByName("DEV_NAME")->DisplayLabel = "Name";
  SemiQuery1->FieldByName("DEV_NAME")->DisplayWidth = 9;
  SemiQuery1->FieldByName("STOCK_LEVEL")->DisplayLabel = "Stock";
  SemiQuery1->FieldByName("STOCK_LEVEL")->DisplayWidth = 5;
  SemiQuery1->FieldByName("DEV_DESCRIP")->DisplayLabel = "Description";
  SemiQuery1->FieldByName("DEV_DESCRIP")->DisplayWidth = 40;
  ShowByTypesCountCaptionLabel->Caption =
    "such devices are in the table.";
  ShowByTypesCountLabel->Caption = IntToStr(SemiQuery1->RecordCount);
}
//---------------------------------------------------------------------
void __fastcall TForm1::ShowAllButtonClick(TObject *Sender)
{
  bool AllowChangeValue;

  // Fake a page change to Page 4 to show all records
  TabbedNotebook1Change (ShowAllButton,4,AllowChangeValue);
}

SQL'S DATA DEFINITION LANGUAGE

  • Data Definition Language(DDL) - handles table and index creation, structure of tables
  • Data Manipulation Language (DML) - handles queries, inserts, deletes, etc.

Creating a Table with SQL

CREATE TABLE SEMINAME
(
	DEV_ID 		INTEGER,
	DEV_NAME 	CHAR(12),
	DEV_TYPE 	CHAR(10),
	DEV_PKG 	CHAR(12),
	DEV_DESCRIP CHAR(50),
	PRIMARY KEY(DEV_ID)
)

Executing SQL Queries

void __fastcall TForm1::CreateButtonClick (TObject *Sender)
{
	UtilityQuery->Close();
	UtilityQuery->SQL->Clear ();
	UtilityQuery->SQL->LoadFromFile("CREATE.SQL");
	UtilityQuery->ExecSQL ();
	UtilityQuery->Close();

Adding a Column to a Table With SQL

void __fastcall TForm1::AddColumnButtonClick (TObject *Sender)
{
	UtilityQuery->Close();
	UtilityQuery->SQL->Clear ();
	UtilityQuery->SQL->Add("ALTER TABLE SEMINAME");
	UtilityQuery->SQL->Add("ADD STOCK_LEVEL INTEGER");
	UtilityQuery->ExecSQL ();
	UtilityQuery->Close();

Creating Indexes With SQL

	UtilityQuery->Close();
	UtilityQuery->SQL->Clear ();
	UtilityQuery->SQL->Add("CREATE INDEX NAMEIDX ON SEMINAME DEV_NAME");
	UtilityQuery->ExecSQL ();
	UtilityQuery->Close();

Adding Records with Insert

	INSERT INTO SEMINAME(DEV_ID,
						DEV_TYPE,
						DEV_NAME,
						DEV_PKG,
						DEV_DESCRIP,
						STOCK_LEVEL,
						SORT_STRING) 
VALUES(:dID, :dName,:dType,:dPkg,:dDescrip,:dLevel,:dSort)

Static and Dynamic SQL Queries

The VALUES clause contains a list of parameters that are identified by a colon

Parameter Assignment and Substitution

each SQL parameter is also a legal C++ identifier and can be assigned values using the ParamByName method
void __fastcall TForm1::AddButtonClick(TObject *Sender)
{
  if (NameField->Text.Length () == 0)
  {
    ShowMessage ("You haven't entered a device name!");
  }
  else
  {
    SemiQuery1->Close ();
    SemiQuery1->SQL->Clear ();
    SemiQuery1->SQL->LoadFromFile("INSERT.SQL");
    SemiQuery1->Prepare ();
    SemiQuery1->ParamByName("dID")->AsInteger = MaxRecordNum + 1;
    SemiQuery1->ParamByName("dName")->AsString = NameField->Text;
    SemiQuery1->ParamByName("dType")->AsString =
      TypesGroup->Items->Strings[TypesGroup->ItemIndex];
    SemiQuery1->ParamByName("dPkg")->AsString = PackageField->Text;
    SemiQuery1->ParamByName("dDescrip")->AsString = DescriptionField->Text;
    SemiQuery1->ParamByName("dLevel")->AsInteger = StrToInt(StockField->Text);
    SemiQuery1->ParamByName("dSort")->AsString = SortStringField->Text;
    SemiQuery1->ExecSQL ();
    SemiQuery1->Close ();
    MaxRecordNum = MaxRecordNum + 1;
    MaxRecLabel->Caption = IntToStr(MaxRecordNum);
    ClearAllEntryFields ();
    UniqueLabel->Caption = "";
    DupeLabel->Caption = "";
  }
}

The Prepare Method

Prepare method forces the SQL query text to the database engine for precompilation


Chapter 18 - Database Odds 'N' Ends

  • SQL's DISTINCT Qualifier

    - see DISTINCT.MAK
  • THE DBLISTBOX and DBCOMBOBOX COMPONENTS

    - see LOOKUP.MAK
  • problems with DBLookups

    - see ARTENTRY.MAK