home *** CD-ROM | disk | FTP | other *** search
/ BUG 15 / BUGCD1998_06.ISO / aplic / jbuilder / jsamples.z / AppDataModule.java < prev    next >
Encoding:
Java Source  |  1997-07-28  |  104.0 KB  |  2,264 lines

  1.  
  2. package borland.samples.intl.application;
  3.  
  4. import java.awt.*;
  5. import java.awt.event.*;
  6. import java.io.*;
  7. import java.sql.*;
  8. import java.text.*;
  9. import java.util.*;
  10. import borland.jbcl.layout.*;
  11. import borland.jbcl.control.*;
  12. import borland.jbcl.model.*;
  13. import borland.jbcl.dataset.*;
  14. import borland.jbcl.util.*;
  15. import borland.jbcl.view.*;
  16.  
  17. import COM.objectspace.jgl.*;
  18.  
  19. import borland.samples.intl.util.*;
  20. import borland.samples.intl.beans.event.*;
  21.  
  22. /**
  23.  * The application's data module, AppDataModule, can be conceptually
  24.  * divided into the following four parts:
  25.  *  - JBCL data access components
  26.  *  - public DataSet accessor methods that are automatically generated 
  27.  *  - event handler logic
  28.  *  - application and business logic
  29.  *
  30.  * The first part consists of JBCL data access components (for example, Database, 
  31.  * QueryDataSet, Column, etc.) which were automatically added to the code and customized
  32.  * using the UI Designer.  These components are declared as instance variables near 
  33.  * the top of the class definition, and are customized within the
  34.  * jbInit() method.  Some properties and custom components that could
  35.  * not be configured using the UI Designer were done in the AppDataModule
  36.  * constructor following the call to jbInit().
  37.  *<p>
  38.  * The second part of the AppDataModule consists of public DataSet
  39.  * accessor methods, such as getCustomerDataSet(), which were
  40.  * automatically generated by the UI Designer for each DataSet in
  41.  * the data module.  Clients of the data module use these accessor
  42.  * methods to obtain references to DataSet components provided by the data
  43.  * module.  For example, the OrderFrame frame uses a data-aware
  44.  * GridControl component to display line items for an order.
  45.  * The GridControl component's dataSet property is set to the
  46.  * OrderLineItemDataSet returned using the getOrderLineItemDataSet() method
  47.  * of the AppDataModule.
  48.  *<p>
  49.  * The third part of the AppDataModule consists of the methods by which the
  50.  * application's GUI components manipulate the state of the
  51.  * application.  For example, when a user presses the Add to Order button of the
  52.  * ProductFrame, the button's actionPerformed handler
  53.  * invokes the AppDataModule component's addProductToOrderLineItem() method.  In
  54.  * response, the data module determines the product currently being
  55.  * viewed in the ProductFrame (the current row in the
  56.  * ProductsDataSet), and adds a new row with the appropriate
  57.  * information to the OrderLineItemDataSet.  Because the  dataSet property
  58.  * of the GridControl in the OrderFrame is assigned to the
  59.  * OrderLineItemDataSet, the new row added to the OrderLineItemDataSet
  60.  * by the data module automatically becomes visible in the
  61.  * GridControl in the OrderFrame .
  62.  *<p>
  63.  * The fourth part of the AppDataModule consists of methods which
  64.  * define the application's logic and business rules.  In general,
  65.  * application logic is implemented within event handlers that are activated in
  66.  * response to DataSet events.  For example, when a new row is
  67.  * inserted into the OrderDataSet, it should be given a unique order
  68.  * number.  To implement this application logic, we first used the
  69.  * UI Designer to create an inserted event handler for the
  70.  * OrderDataSet.  (When AppDataModule.java is open in the UI Designer, select 
  71.  * OrderDataSet in the Component Tree, select the Events tab of
  72.  * the Inspector, and enter an event handler name for the
  73.  * OrderDataSet class's inserted event property).  Inside the event
  74.  * handler code, we assign a unique value to the order_no column of
  75.  * the newly inserted row.
  76.  *<p>
  77.  * Business rules are usually defined by setting validation
  78.  * constraints on columns of a DataSet.  For example, to specify that
  79.  * the quantity of an item ordered be greater than 0, we set the min
  80.  * property of the quantity column of the OrderLineItemDataSet to the
  81.  * value '1'.  Doing so prevents a row from being posted if the
  82.  * quantity is not at least 1.
  83.  *<p>
  84.  * To allow AppDataModule to use either a JDBC database
  85.  * or a text data file as its data provider, we took advantage of the
  86.  * fact that a QueryDataSet also has a dataFile property.  Using the
  87.  * Designer, we defined both a TextDataFile and query for each
  88.  * QueryDataSet, and set the executeOnOpen property of each query to
  89.  * be false.  Doing so allows data to be provided from each
  90.  * QueryDataSet component's TextDataFile data source by default.  In order to
  91.  * use a JDBC database as the provider, we provide a static method
  92.  * (setJDBCDataSource) which sets JDBC connection information.  At
  93.  * runtime, if JDBC connection information has been set before the
  94.  * AppDataModule is instantiated, we provide data for each
  95.  * QueryDataSet by executing the query instead of loading data from a
  96.  * TextDataFile.
  97.  */
  98. public class AppDataModule implements DataModule, LocaleChangeListener{
  99.   /**
  100.    * Single instance of the AppDataModule used by the application.
  101.    * See getDataModule() for more info.
  102.    */
  103.   private static AppDataModule appDataModule = null;
  104.  
  105.   /**
  106.    * Reference to the TextRes resource bundle which contains localized
  107.    * resources for the GUI-independent side of the application.  See
  108.    * borland\samples\intl\application\resources\TextRes.java for more
  109.    * information.
  110.    */
  111.   ResourceBundle textRes = java.util.ResourceBundle.getBundle("borland.samples.intl.application.resources.TextRes");
  112.  
  113.   /**
  114.    * Determines whether the application's data sources will be  all text file based
  115.    * or all JDBC database based.  It is automatically set to true
  116.    * if a JDBC data source is specified by a call to AppDataModule's
  117.    * static setJDBCDataSource() method before is
  118.    * instantiated.
  119.    */
  120.   private static boolean useQueryDataSet = false;
  121.  
  122.   /**
  123.    * Contains the URL for a JDBC database connection.  Set via the
  124.    * static setJDBCDataSource() method.
  125.    */
  126.   private static String jdbcURL;
  127.  
  128.   /**
  129.    * Contains the user name to a JDBC database connection.  Set via
  130.    * the static setJDBCDataSource() method.
  131.    */
  132.   private static String jdbcUser;
  133.  
  134.   /**
  135.    * Contains the password to a JDBC database connection.  Set via
  136.    * the static setJDBCDataSource() method.
  137.    */
  138.   private static String jdbcPassword;
  139.  
  140.   /**
  141.    * Contains the driver name for a JDBC database connection.  Set
  142.    * via the static setJDBCDataSource() method.
  143.    */
  144.   private static String jdbcDriver;
  145.  
  146.   /**
  147.    * Public constant which may be passed as the product filtering
  148.    * category argument of filterProducts() to signify that no products
  149.    * should be filtered out of ProductsDataSet.
  150.    */
  151.   public static final int NO_PRODUCT_FILTER = -1;
  152.  
  153.   /**
  154.    * Contains the value used to decide which rows to filter in/out of
  155.    * the ProductsDataSet DataSet.  See the comments for
  156.    * productDataSet_filterRow() for more information.
  157.    */
  158.   private int productFilteringCategory = NO_PRODUCT_FILTER;
  159.  
  160.   /**
  161.    * Constant used to signify that no customers should be filtered
  162.    * into customerDataSet.
  163.    */
  164.   private static int INVALID_CUSTOMER = -1;
  165.  
  166.   /**
  167.    * Holds the customer number value of the customer for the current
  168.    * order.
  169.    */
  170.   private static int queryCustomerNo = INVALID_CUSTOMER;
  171.  
  172.   /**
  173.    * Indicates whether or not a new row has already been inserted into
  174.    * OrderDataSet and CustomerDataSet in preparation for a new order
  175.    * entry.
  176.    */
  177.   protected boolean enteringOrder = false;
  178.  
  179.   /**
  180.    * Determines whether or not an 'inserting' event should be allowed
  181.    * to succeed on a DataSet.  By throwing a VetoException from within
  182.    * a DataSet's 'inserting' event handler, if vetoInsertingEvent is
  183.    * 'true', we can prevent users from inserting a new row by pressing
  184.    * {Ctrl-Ins} from within a GridControl or FieldControl.
  185.    */
  186.   protected boolean vetoInsertingEvent = true;
  187.  
  188.   /**
  189.    * Determines whether or not a 'deleting' event should be allowed to
  190.    * succeed on a data set.  By throwing a VetoException from within a
  191.    * DataSet's 'deleting' event handler, if vetoDeletingEvent is
  192.    * 'true', we can prevent users from deleting existing rows by
  193.    * pressing {Ctrl-Del} from within a GridControl or FieldControl.
  194.    */
  195.   protected boolean vetoDeletingEvent = true;
  196.  
  197.   /**
  198.    * Determines whether or not an 'adding' or 'updating' event should
  199.    * be allowed to succeed on a data set.  By throwing a VetoException
  200.    * from within a DataSet's 'adding' or 'updating' event handler, if
  201.    * vetoAddingOrUpdatingEvent is 'true', we can prevent users from
  202.    * posting existing rows by pressing {PgDn} or {PgUp} from within a
  203.    * FieldControl.
  204.    */
  205.   protected boolean vetoAddingOrUpdatingEvent = true;
  206.  
  207.   /**
  208.    * Custom (model-view) item editor for the 'quantity' column of the
  209.    * orderLineItemDataSet DataSet.  See
  210.    * borland/samples/intl/util/IntegerSpinEditor.java for more info.
  211.    */
  212.   IntegerSpinEditor quantityItemEditor = new IntegerSpinEditor();
  213.  
  214.   /**
  215.    * The custom (model-view) item editor for the 'details' column of
  216.    * orderLineItemDataSet.  See
  217.    * borland/samples/intl/application/ResourceableLineItemDetailEditor.java
  218.    * for more info.
  219.    */
  220.   ResourceableLineItemDetailEditor orderLineItemDetailEditor = new ResourceableLineItemDetailEditor();
  221.  
  222.   /**
  223.    * The custom (model-view) item painter for the 'details' column of
  224.    * orderLineItemDataSet.  See
  225.    * borland/samples/intl/application/ResourceableLineItemDetailPainter.java
  226.    * for more info.
  227.    */
  228.   ResourceableLineItemDetailPainter orderLineItemDetailPainter = new ResourceableLineItemDetailPainter();
  229.  
  230.   /**
  231.    * Custom (model-view) item painter for the calculated 'description'
  232.    * lookup column of the orderLineItemDataSet DataSet.  See
  233.    * borland/samples/intl/util/ResourceableTextItemPainter.java for
  234.    * more info.
  235.    */
  236.   ResourceableTextItemPainter descriptionLookupItemPainter = new ResourceableTextItemPainter();
  237.  
  238.   // The following AnnotatedEmptyItemPainters are custom (model-view)
  239.   // item painters used to put the 'required' message in empty,
  240.   // required fields in the orderDataSet and customerDataSet data sets.
  241.   // The following item painters were dropped into the data module
  242.   // and assigned to be item painters for columns using the UI Designer.
  243.   // See borland/samples/intl/util/AnnotatedEmptyItemPainter.java for
  244.   // more info.
  245.   AnnotatedEmptyItemPainter lastNameRequiredPainter = new AnnotatedEmptyItemPainter();
  246.   AnnotatedEmptyItemPainter firstNameRequiredPainter = new AnnotatedEmptyItemPainter();
  247.   AnnotatedEmptyItemPainter address1RequiredPainter = new AnnotatedEmptyItemPainter();
  248.   AnnotatedEmptyItemPainter cityRequiredPainter = new AnnotatedEmptyItemPainter();
  249.   AnnotatedEmptyItemPainter provinceRequiredPainter = new AnnotatedEmptyItemPainter();
  250.   AnnotatedEmptyItemPainter paymentMethodRequiredPainter = new AnnotatedEmptyItemPainter();
  251.   AnnotatedEmptyItemPainter cardExpirationDateRequiredPainter = new AnnotatedEmptyItemPainter();
  252.   AnnotatedEmptyItemPainter creditCardNoRequiredPainter = new AnnotatedEmptyItemPainter();
  253.   AnnotatedEmptyItemPainter countryRequiredPainter = new AnnotatedEmptyItemPainter();
  254.  
  255.   // The following components were added to the data module
  256.   // automatically by the UI Designer when we dropped data access
  257.   // components into the data module.
  258.   Database database = new Database();
  259.   TextDataFile customerDataFile = new TextDataFile();
  260.  
  261.   /**
  262.    * DataSet used to access customer data from either customerDataFile
  263.    * or a query on the Customers table.  It is row filtered to only
  264.    * include the single customer row which should be visible/editable
  265.    * at any time.
  266.    */
  267.   QueryDataSet customerDataSet = new QueryDataSet();
  268.   Column lastNameColumn = new Column();
  269.   Column middleNameColumn = new Column();
  270.   Column firstNameColumn = new Column();
  271.   Column address1Column = new Column();
  272.   Column address2Column = new Column();
  273.   Column customerNoColumn = new Column();
  274.   Column cityColumn = new Column();
  275.   Column provinceColumn = new Column();
  276.   Column countryColumn = new Column();
  277.   Column emailColumn = new Column();
  278.   Column phoneColumn = new Column();
  279.   Column faxColumn = new Column();
  280.   Column passwordColumn = new Column();
  281.   Column postalCodeColumn = new Column();
  282.  
  283.   /**
  284.    * DataSetView which uses customerDataSet as its StorageDataSet.
  285.    * Unlike customerDataSet, it is not restricted to viewing only a
  286.    * single row of the customerDataSet, and therefore is used in
  287.    * CustomerLookupDialog.java as the DataSet of a GridControl which
  288.    * shows the entire list of customers.
  289.    */
  290.   DataSetView customerDataSetView = new DataSetView();
  291.  
  292.   TextDataFile exchangeRateDataFile = new TextDataFile();
  293.   
  294.   QueryDataSet exchangeRateDataSet = new QueryDataSet();
  295.   Column exchangeRateColumn = new Column();
  296.  
  297.   TextDataFile paymentMethodDataFile = new TextDataFile();
  298.  
  299.   QueryDataSet paymentMethodDataSet = new QueryDataSet();
  300.   
  301.   TextDataFile orderDataFile = new TextDataFile();
  302.  
  303.   QueryDataSet orderDataSet = new QueryDataSet();
  304.   Column orderDateColumn = new Column();
  305.   Column paymentMethodColumn = new Column();
  306.   Column creditCardNoColumn = new Column();
  307.   Column cardExpirationDateColumn = new Column();
  308.  
  309.   TextDataFile orderLineItemDataFile = new TextDataFile();
  310.  
  311.   QueryDataSet orderLineItemDataSet = new QueryDataSet();
  312.   Column skuColumn = new Column();
  313.   Column quantityColumn = new Column();
  314.   Column detailsColumn = new Column();
  315.   Column descriptionLookupColumn = new Column();
  316.   Column unitPriceLineItemColumn = new Column();
  317.   Column extdPriceColumn = new Column();
  318.   Column localExtdPriceColumn = new Column();
  319.   Column subtotalColumn = new Column();
  320.   Column localSubtotalColumn = new Column();
  321.   Column shippingColumn = new Column();
  322.   Column localShippingColumn = new Column();
  323.   Column taxColumn = new Column();
  324.   Column localTaxColumn = new Column();
  325.   Column totalColumn = new Column();
  326.   Column localTotalColumn = new Column();
  327.  
  328.   TextDataFile productsDataFile = new TextDataFile();
  329.  
  330.   QueryDataSet productsDataSet = new QueryDataSet();
  331.   Column descriptionColumn = new Column();
  332.   Column unitPriceProductsColumn = new Column();
  333.   
  334.   TextDataFile productColorsDataFile = new TextDataFile();
  335.  
  336.   QueryDataSet productColorsDataSet = new QueryDataSet();
  337.  
  338.   TextDataFile productSizesDataFile = new TextDataFile();
  339.  
  340.   QueryDataSet productSizesDataSet = new QueryDataSet();
  341.  
  342.   TextDataFile customerNoDataFile = new TextDataFile();
  343.  
  344.   QueryDataSet customerNoDataSet = new QueryDataSet();
  345.  
  346.   TextDataFile orderNoDataFile = new TextDataFile();
  347.  
  348.   QueryDataSet orderNoDataSet = new QueryDataSet();
  349.   Column imageColumn = new Column();
  350.   Column orderNoColumn = new Column();
  351.   Column orderLineItemNoColumn = new Column();
  352.   Column totalQuantityColumn = new Column();
  353.  
  354.   /**
  355.    * Initializes data-access components of the data module.  
  356.    * Components configurable via the UI Designer are set within jbInit(),
  357.    * all others are set in the code following the call to jbInit().
  358.    */
  359.   public AppDataModule() {
  360.     try {
  361.       jbInit();
  362.  
  363.       // The remaining code configures column properties and
  364.       // initializes DataSets in ways which could not be done using
  365.       // the UI Designer.
  366.  
  367.       // NOTE: Columns of a data set are cloned by
  368.       // DataSet.setColumns().  Thus maintaining a reference to the
  369.       // column object and updating its properties will have no effect
  370.       // on the column's DataSet once setColumns() has been called.
  371.       // In order to update column properties after setColumns() has
  372.       // been called, you must either call setColumns() again, or use
  373.       // DataSet.getColumn() to get the column and call
  374.       // DataSet.enableDataSetEvents(true) to force the DataSet to
  375.       // update its column properties.
  376.  
  377.       // If setJDBCDataSource() was called prior to initial
  378.       // instantiation of the AppDataModule, useQueryDataSet will have
  379.       // the value 'true' and we will try to use its passed parameters
  380.       // to load data from a JDBC source.
  381.  
  382.       if (useQueryDataSet) {
  383.     // Explicitly load the JDBC Driver
  384.     Class.forName(jdbcDriver);
  385.     // Don't pass a driver to ConnectionDescriptor because it will lookup
  386.     // the system classpath property (causing an applet security violation) to
  387.     // locate the driver.
  388.     database.setConnection(new borland.jbcl.dataset.ConnectionDescriptor(jdbcURL, jdbcUser, jdbcPassword, false, ""));
  389.       }
  390.  
  391.       // Set aggregation descriptors for aggregated columns (columns
  392.       // for which the calcType property is CalcType.AGGREGATE).
  393.       // The first argument to the AggDescriptor is the list
  394.       // of columns on which to group.  Since we want to compute the
  395.       // sum of values for rows in the orderLineItemDataSet (the
  396.       // line item rows for each order), we specify the linking
  397.       // column 'order_no' as the grouping column.  
  398.       // The following AggDescriptor specifies that 
  399.       // SumAggOperator() should be applied to the 'extd_price' value
  400.       // in all rows of the orderLineItemDataSet grouped by 'order_no', and
  401.       // the aggregated result assigned to subtotal.
  402.       orderLineItemDataSet.getColumn("subtotal").setAgg(new AggDescriptor(new String [] { "order_no" }, "extd_price", new SumAggOperator()));
  403.  
  404.       // The following AggDescriptor specifies that the
  405.       // SumAggOperator() should be applied to the 'quantity' value in
  406.       // all rows of the orderLineItemDataSet grouped by 'order_no' and the
  407.       // aggregated result assigned to the totalQuantityColumn.
  408.       orderLineItemDataSet.getColumn("total_quantity").setAgg(new AggDescriptor(new String [] { "order_no" }, "quantity", new SumAggOperator()));
  409.  
  410.       // Set aggregation descriptors for columns whose values
  411.       // are based on calculations performed on
  412.       // aggregated values.  The calcType property of a 'custom' aggregated
  413.       // column must be set to CalcType.AGGREGATE.  An AggDescriptor
  414.       // for a calculated aggregated column differs from an
  415.       // AggDescriptor for a regular aggregated column in that an
  416.       // aggregate operator and aggregate value column name are both
  417.       // set to 'null'.  When a custom aggregate value must be
  418.       // recalculated, the CalcAggFieldsListener registered with
  419.       // the data set is automatically invoked.
  420.       orderLineItemDataSet.getColumn("local_subtotal").setAgg(new AggDescriptor(new String [] { "order_no" }, null, null));
  421.       orderLineItemDataSet.getColumn("shipping").setAgg(new AggDescriptor(new String [] { "order_no" }, null, null));
  422.       orderLineItemDataSet.getColumn("local_shipping").setAgg(new AggDescriptor(new String [] { "order_no" }, null, null));
  423.       orderLineItemDataSet.getColumn("tax").setAgg(new AggDescriptor(new String [] { "order_no" }, null, null));
  424.       orderLineItemDataSet.getColumn("local_tax").setAgg(new AggDescriptor(new String [] { "order_no" }, null, null));
  425.       orderLineItemDataSet.getColumn("total").setAgg(new AggDescriptor(new String [] { "order_no" }, null, null));
  426.       orderLineItemDataSet.getColumn("local_total").setAgg(new AggDescriptor(new String [] { "order_no" }, null, null));
  427.  
  428.       // Set the string to be displayed by the AnnotatedEmptyItemPainter for 
  429.       // required columns when empty.  Also, replace the default item painter
  430.       // used by an AnnotatedEmptyItemPainter (TextItemPainter) with a
  431.       // focusable, selectable text item painter.
  432.       String requiredString = textRes.getString("value_required");
  433.       lastNameRequiredPainter.setDisplayString(requiredString);
  434.       lastNameRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  435.       firstNameRequiredPainter.setDisplayString(requiredString);
  436.       firstNameRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  437.       address1RequiredPainter.setDisplayString(requiredString);
  438.       address1RequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  439.       cityRequiredPainter.setDisplayString(requiredString);
  440.       cityRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  441.       provinceRequiredPainter.setDisplayString(requiredString);
  442.       provinceRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  443.       countryRequiredPainter.setDisplayString(requiredString);
  444.       countryRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  445.       paymentMethodRequiredPainter.setDisplayString(requiredString);
  446.       paymentMethodRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  447.       creditCardNoRequiredPainter.setDisplayString(requiredString);
  448.       creditCardNoRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  449.       cardExpirationDateRequiredPainter.setDisplayString(requiredString);
  450.       cardExpirationDateRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  451.  
  452.       // Set the resource bundle used by descriptionLookupItemPainter,
  453.       // the item painter used to display localized product
  454.       // descriptions in the 'description' column of the
  455.       // orderLineItemDataSet DataSet.  Also, replace
  456.       // descriptionLookupItemPainter's default TextItemPainter with a
  457.       // selectable one, and assign it as the item painter for the
  458.       // descriptionLookupColumn (wrapped within a focusable item
  459.       // painter).
  460.       descriptionLookupItemPainter.setResourceBundle(textRes);
  461.       descriptionLookupItemPainter.setTextItemPainter(new SelectableTextItemPainter());
  462.       orderLineItemDataSet.getColumn("description").setItemPainter(new FocusableItemPainter(descriptionLookupItemPainter));
  463.  
  464.       // The orderLineItemDetailEditor and orderLineItemDetailPainter classes 
  465.       // are responsible for managing the editing and display of localized
  466.       // data from the details column of orderLineItemDataSet.  More
  467.       // specifically, orderLineItemDetailEditor reads non-localized
  468.       // product color and size values from the column, and presents
  469.       // the user with one or more ChoiceControls to edit the values.
  470.       // Similarly, the orderLineItemDetailPainter reads non-localized
  471.       // product color and size values from the column, and displays
  472.       // their localized translations.  Because this functionality
  473.       // requires intimate knowledge of the application's format for
  474.       // storing details data, orderLineItemDetailEditor and
  475.       // orderLineItemDetailPainter are located within the
  476.       // borland.samples.intl.application package rather than the
  477.       // general borland.samples.intl.util package.
  478.       orderLineItemDetailEditor.setAppDataModule(this);
  479.       orderLineItemDetailEditor.setResourceBundle(textRes);
  480.       orderLineItemDetailPainter.setResourceBundle(textRes);
  481.  
  482.       // Set localized display masks for date and currency columns of
  483.       // the orderDataSet for the default locale.  If a locale change
  484.       // occurs, code within localeChanged() will reset the masks for
  485.       // the new locale.
  486.       orderDataSet.getColumn("order_date").setDisplayMask(((SimpleDateFormat) SimpleDateFormat.getDateInstance(DateFormat.DEFAULT, Locale.getDefault())).toPattern());
  487.       orderLineItemDataSet.getColumn("local_extd_price").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault())).toPattern());
  488.       orderLineItemDataSet.getColumn("local_subtotal").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault())).toPattern());
  489.       orderLineItemDataSet.getColumn("local_shipping").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault())).toPattern());
  490.       orderLineItemDataSet.getColumn("local_tax").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault())).toPattern());
  491.       orderLineItemDataSet.getColumn("local_total").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault())).toPattern());
  492.       
  493.       // Provide an edit mask which allows the user to only enter the
  494.       // month and year for a credit card's expiration date.  When
  495.       // using such an edit mask, the day is automatically set to the
  496.       // first of the month.  Thus we set the minimum date for the
  497.       // credit card expiration column to be the last day of the
  498.       // previous month.
  499.       Variant variant = new Variant();
  500.       Calendar calendar = new GregorianCalendar();
  501.       variant.setTimestamp(new java.sql.Timestamp(calendar.get(Calendar.YEAR)-1900, calendar.get(Calendar.MONTH), 1, 0, 0, 0, 0));
  502.       orderDataSet.getColumn("card_expiration_date").setMinValue(variant);
  503.  
  504.       if (useQueryDataSet) {
  505.  
  506.     // The following query is used for two different purposes.
  507.     // First, we pass it a non-existent customer_no to provide an empty set of order
  508.     // rows into orderDataSet and orderLineItemDataSet.  We then insert new 
  509.     // rows into the data sets.  By doing the query, even though it
  510.     // retrieves no rows, we load the metadata info into the DataSet, so that we
  511.     // can resolve it back to the data source using DataSet.saveChanges(DataSet).
  512.     // Second, this query is also used to select all of a customer's orders for
  513.     // browsing.  Since we don't allow a customer to make any changes to existing
  514.     // orders, resolving is not necessary in this case.
  515.     ParameterRow orderDataSetQueryParameters = new ParameterRow();
  516.     orderDataSetQueryParameters.addColumn("customer_no", Variant.INT);
  517.     orderDataSetQueryParameters.setInt("customer_no", queryCustomerNo);
  518.     orderDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from orders where customer_no = :customer_no", orderDataSetQueryParameters, false, false));
  519.       } else {
  520.  
  521.     // When using a QueryDataSet, we simply execute a query to provide order rows
  522.     // into the orderDataSet when browsing a customer's previous orders.
  523.     // But for a text data file source, we always have all order rows in the
  524.     // data set.  To restrict the user to navigating only a particular customer's 
  525.     // order data, we add a row filter and restrict navigation to order rows for 
  526.     // the current customer (stored in the queryCustomerNo variable).
  527.     orderDataSet.addRowFilterListener(new AppDataModule_orderDataSet_rowFilterAdapter(this));
  528.       }
  529.  
  530.       // Provide data set data from the appropriate data sources.
  531.       if (useQueryDataSet) {
  532.     // If we're using QueryDataSets, we first need to set the LoadOnOpen property of each
  533.     // DataSet's corresponding TextDataFile to false, so that opening the DataSet will
  534.     // not load data from the TextDataFile.  Then in order to provide data from the
  535.     // JDBC data provider, we execute the query.
  536.     customerDataFile.setLoadOnOpen(false);
  537.     customerDataSet.executeQuery();
  538.     customerDataSet.open();
  539.     exchangeRateDataFile.setLoadOnOpen(false);
  540.     exchangeRateDataSet.executeQuery();
  541.     exchangeRateDataSet.open();
  542.     paymentMethodDataFile.setLoadOnOpen(false);
  543.     paymentMethodDataSet.executeQuery();
  544.     paymentMethodDataSet.open();
  545.     orderDataFile.setLoadOnOpen(false);
  546.     orderDataSet.executeQuery();
  547.     orderDataSet.open();
  548.     orderLineItemDataFile.setLoadOnOpen(false);
  549.     orderLineItemDataSet.executeQuery();
  550.     orderLineItemDataSet.open();
  551.     productsDataFile.setLoadOnOpen(false);
  552.     productsDataSet.executeQuery();
  553.     productsDataSet.open();
  554.     productColorsDataFile.setLoadOnOpen(false);
  555.     productColorsDataSet.executeQuery();
  556.     productColorsDataSet.open();
  557.     productSizesDataFile.setLoadOnOpen(false);
  558.     productSizesDataSet.executeQuery();
  559.     productSizesDataSet.open();
  560.     customerNoDataFile.setLoadOnOpen(false);
  561.     orderNoDataFile.setLoadOnOpen(false);
  562.       } else {
  563.     // Open the DataSets we plan to use in subsequent operations.
  564.     // When using a TextDataFile, the default value for the 'loadOnOpen'
  565.     // property is true, so opening the data set will automatically load
  566.     // data from the text file.
  567.     customerDataSet.open();
  568.     exchangeRateDataSet.open();
  569.     paymentMethodDataSet.open();
  570.     orderDataSet.open();
  571.     orderLineItemDataSet.open();
  572.     productsDataSet.open();
  573.     productColorsDataSet.open();
  574.     productSizesDataSet.open();
  575.       }
  576.  
  577.       customerDataSetView.open();
  578.  
  579.       // Now that the paymentMethodDataSet is open, set the default
  580.       // credit card name and corresponding card no digit edit mask
  581.       // from the paymentMethodDataSet.
  582.       orderLineItemDataSet.enableDataSetEvents(false);
  583.       orderDataSet.enableDataSetEvents(false);
  584.       orderDataSet.close();
  585.       orderDataSet.getColumn("credit_card_no").setEditMask(paymentMethodDataSet.getString("digit_pattern"));
  586.       orderDataSet.getColumn("payment_method").setDefault(paymentMethodDataSet.getString("credit_card_name"));
  587.       orderDataSet.open();
  588.       orderLineItemDataSet.enableDataSetEvents(true);
  589.       orderDataSet.enableDataSetEvents(true);
  590.  
  591.       // Because descriptions for products in the productsDataSet are localized,
  592.       // we provide a value for the productDataSet's 'description' column by
  593.       // using a product's 'sku' number as its lookup key in a resource bundle.
  594.       // We then add that value into the 'description' column.
  595.       // When using a TextDataFile data source, we also need to explicitly
  596.       // fill the 'image' column with image data, since binary data cannot be
  597.       // stored in a text file.
  598.       int sku;
  599.       String imageFileName;
  600.       String localizedDescription;
  601.  
  602.       // Iterate over all rows in productsDataSet 
  603.       productsDataSet.first();
  604.       while (productsDataSet.inBounds()) {
  605.     sku = productsDataSet.getInt("sku");
  606.  
  607.     // Add localized description data
  608.     localizedDescription = textRes.getString("" + sku);
  609.     productsDataSet.setString("description", localizedDescription);
  610.  
  611.     // Since we can't save binary image data within a text data
  612.     // file, when using a text data file as our data source we
  613.     // need to import the image data separately.
  614.     if (!useQueryDataSet) {
  615.       imageFileName = "data\\" + sku + ".gif";
  616.       BufferedInputStream bufferedInputStream = 
  617.         new BufferedInputStream(this.getClass().getResourceAsStream(imageFileName));
  618.       bufferedInputStream.mark(40000);
  619.       productsDataSet.setBinaryStream("image", bufferedInputStream);
  620.     }
  621.     productsDataSet.next();
  622.       }
  623.       // Since productsDataSet.next() will fail on the last row
  624.       // (allowing us to exit the loop), we need to explicitly post
  625.       // changes which would have been posted had it been successful.
  626.       productsDataSet.post();
  627.       productsDataSet.first();
  628.  
  629.       // Load the current US dollar exchange rate our of exchangeRateDataSet
  630.       readExchangeRate();
  631.  
  632.       // Register ourselves as a locale change listener with the system locale change manager
  633.       LocaleChangeManager.getLocaleChangeManager().addLocaleChangeListener(this);
  634.  
  635.       // Install a custom DataSetException error handler to display localized messages
  636.       DataSetException.addExceptionListener(new ResourcedDataSetExceptionHandler(this));
  637.     }
  638.     catch (Exception e) {
  639.       borland.jbcl.util.Diagnostic.printStackTrace(e);
  640.     }
  641.   }
  642.  
  643.   /**
  644.    * Contains components customized using the UI Designer.
  645.    */
  646.   void jbInit() throws Exception{
  647.     lastNameColumn.setCaption(textRes.getString("last_name"));
  648.     lastNameColumn.setColumnName("last_name");
  649.     lastNameColumn.setDataType(borland.jbcl.util.Variant.STRING);
  650.     lastNameColumn.setEditMask("cccccccccccccccccccccccccccccc");
  651.     lastNameColumn.setItemPainter(lastNameRequiredPainter);
  652.     lastNameColumn.setRequired(true);
  653.     middleNameColumn.setCaption(textRes.getString("middle_name"));
  654.     middleNameColumn.setColumnName("middle_name");
  655.     middleNameColumn.setDataType(borland.jbcl.util.Variant.STRING);
  656.     middleNameColumn.setEditMask("cccccccccccccccccccccccccccccc");
  657.     firstNameColumn.setCaption(textRes.getString("first_name"));
  658.     firstNameColumn.setColumnName("first_name");
  659.     firstNameColumn.setDataType(borland.jbcl.util.Variant.STRING);
  660.     firstNameColumn.setEditMask("cccccccccccccccccccccccccccccc");
  661.     firstNameColumn.setItemPainter(firstNameRequiredPainter);
  662.     firstNameColumn.setRequired(true);
  663.     address1Column.setCaption(textRes.getString("address1"));
  664.     address1Column.setColumnName("address1");
  665.     address1Column.setDataType(borland.jbcl.util.Variant.STRING);
  666.     address1Column.setEditMask("cccccccccccccccccccccccccccccccccccccccccccccccccc");
  667.     address1Column.setVisible(borland.jbcl.util.TriState.NO);
  668.     address1Column.setItemPainter(address1RequiredPainter);
  669.     address1Column.setRequired(true);
  670.     address2Column.setCaption(textRes.getString("address2"));
  671.     address2Column.setColumnName("address2");
  672.     address2Column.setDataType(borland.jbcl.util.Variant.STRING);
  673.     address2Column.setEditMask("cccccccccccccccccccccccccccccccccccccccccccccccccc");
  674.     address2Column.setVisible(borland.jbcl.util.TriState.NO);
  675.     customerNoColumn.setColumnName("customer_no");
  676.     customerNoColumn.setDataType(borland.jbcl.util.Variant.INT);
  677.     customerNoColumn.setVisible(borland.jbcl.util.TriState.NO);
  678.     cityColumn.setCaption(textRes.getString("city"));
  679.     cityColumn.setColumnName("city");
  680.     cityColumn.setDataType(borland.jbcl.util.Variant.STRING);
  681.     cityColumn.setEditMask("cccccccccccccccccccccccccccccc");
  682.     cityColumn.setItemPainter(cityRequiredPainter);
  683.     cityColumn.setRequired(true);
  684.     provinceColumn.setCaption(textRes.getString("province"));
  685.     provinceColumn.setColumnName("province");
  686.     provinceColumn.setDataType(borland.jbcl.util.Variant.STRING);
  687.     provinceColumn.setEditMask(textRes.getString("province_editmask"));
  688.     provinceColumn.setItemPainter(provinceRequiredPainter);
  689.     provinceColumn.setRequired(true);
  690.     countryColumn.setCaption(textRes.getString("country"));
  691.     countryColumn.setColumnName("country");
  692.     countryColumn.setDataType(borland.jbcl.util.Variant.STRING);
  693.     countryColumn.setEditMask("cccccccccccccccccccccccccccccc");
  694.     countryColumn.setItemPainter(countryRequiredPainter);
  695.     countryColumn.setRequired(true);
  696.     emailColumn.setCaption(textRes.getString("e_mail"));
  697.     emailColumn.setColumnName("e_mail");
  698.     emailColumn.setDataType(borland.jbcl.util.Variant.STRING);
  699.     emailColumn.setEditMask("cccccccccccccccccccccccccccccccccccccccccccccccccc");
  700.     emailColumn.setVisible(borland.jbcl.util.TriState.NO);
  701.     phoneColumn.setCaption(textRes.getString("phone"));
  702.     phoneColumn.setColumnName("phone");
  703.     phoneColumn.setDataType(borland.jbcl.util.Variant.STRING);
  704.     phoneColumn.setEditMask(textRes.getString("phone_editmask"));
  705.     phoneColumn.setVisible(borland.jbcl.util.TriState.NO);
  706.     faxColumn.setCaption(textRes.getString("fax"));
  707.     faxColumn.setColumnName("fax");
  708.     faxColumn.setDataType(borland.jbcl.util.Variant.STRING);
  709.     faxColumn.setEditMask(textRes.getString("phone_editmask"));
  710.     faxColumn.setVisible(borland.jbcl.util.TriState.NO);
  711.     passwordColumn.setColumnName("mangled_password");
  712.     passwordColumn.setDataType(borland.jbcl.util.Variant.STRING);
  713.     passwordColumn.setVisible(borland.jbcl.util.TriState.NO);
  714.     postalCodeColumn.setCaption(textRes.getString("postal_code"));
  715.     postalCodeColumn.setColumnName("postal_code");                    
  716.     postalCodeColumn.setDataType(borland.jbcl.util.Variant.STRING);
  717.     postalCodeColumn.setEditMask(textRes.getString("postal_code_editmask"));
  718.     customerDataSetView.setStorageDataSet(customerDataSet);
  719.     exchangeRateDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\ExchangeRates.txt");
  720.     exchangeRateDataSet.setDataFile(exchangeRateDataFile);
  721.     exchangeRateDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from exchangeRates", null, false, false));
  722.     exchangeRateColumn.setColumnName("exchange_rate");
  723.     exchangeRateColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  724.     exchangeRateColumn.setEditMask("#####{.#####}");
  725.     exchangeRateColumn.addColumnChangeListener(new AppDataModule_exchangeRateColumn_columnChangeAdapter(this));
  726.     paymentMethodDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\PaymentMethods.txt");
  727.     paymentMethodDataSet.setDataFile(paymentMethodDataFile);
  728.     paymentMethodDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from paymentMethods", null, false, false));
  729.     orderDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\Orders.txt");
  730.     orderDataSet.setDataFile(orderDataFile);
  731.     orderDataSet.addEditListener(new AppDataModule_orderDataSet_editAdapter(this));
  732.     orderDateColumn.setColumnName("order_date");
  733.     orderDateColumn.setDataType(borland.jbcl.util.Variant.TIMESTAMP);
  734.     orderDateColumn.setDefault("now");
  735.     paymentMethodColumn.setColumnName("payment_method");
  736.     paymentMethodColumn.setDataType(borland.jbcl.util.Variant.STRING);
  737.     paymentMethodColumn.setItemPainter(paymentMethodRequiredPainter);
  738.     paymentMethodColumn.setRequired(true);
  739.     paymentMethodColumn.addColumnChangeListener(new AppDataModule_paymentMethodColumn_columnChangeAdapter(this));
  740.     cardExpirationDateColumn.setColumnName("card_expiration_date");
  741.     cardExpirationDateColumn.setDataType(borland.jbcl.util.Variant.TIMESTAMP);
  742.     cardExpirationDateColumn.setEditMask(textRes.getString("month_year_editmask"));
  743.     cardExpirationDateColumn.setDisplayMask(textRes.getString("month_year_editmask"));
  744. //    cardExpirationDateColumn.setItemPainter(cardExpirationDateRequiredPainter);
  745.     cardExpirationDateColumn.setRequired(true);
  746.     creditCardNoColumn.setColumnName("credit_card_no");
  747.     creditCardNoColumn.setDataType(borland.jbcl.util.Variant.STRING);
  748.     creditCardNoColumn.setItemPainter(creditCardNoRequiredPainter);
  749.     creditCardNoColumn.setRequired(true);
  750.     orderLineItemDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\OrderLineItems.txt");
  751.     orderLineItemDataSet.setDataFile(orderLineItemDataFile);
  752.     orderLineItemDataSet.setMasterLink(new borland.jbcl.dataset.MasterLinkDescriptor(orderDataSet, new String[] {"order_no"}, new String[] {"order_no"}, false));
  753.     orderLineItemDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from orderLineItems", null, false, false));
  754.     orderLineItemDataSet.addEditListener(new AppDataModule_orderLineItemDataSet_editAdapter(this));
  755.     orderLineItemDataSet.addCalcAggFieldsListener(new AppDataModule_orderLineItemDataSet_calcAggFieldsAdapter(this));
  756.     orderLineItemDataSet.addCalcFieldsListener(new AppDataModule_orderLineItemDataSet_calcFieldsAdapter(this));
  757.     quantityColumn.setCaption(textRes.getString("quantity"));
  758.     quantityColumn.setColumnName("quantity");
  759.     quantityColumn.setDataType(borland.jbcl.util.Variant.INT);
  760.     quantityColumn.setItemEditor(quantityItemEditor);
  761.     quantityColumn.setMin("1");
  762.     quantityColumn.addColumnChangeListener(new AppDataModule_quantityColumn_columnChangeAdapter(this));
  763.     detailsColumn.setCaption(textRes.getString("details"));
  764.     detailsColumn.setColumnName("details");
  765.     detailsColumn.setDataType(borland.jbcl.util.Variant.STRING);
  766.     detailsColumn.setItemEditor(orderLineItemDetailEditor);
  767.     detailsColumn.setItemPainter(orderLineItemDetailPainter);
  768.     descriptionLookupColumn.setCaption(textRes.getString("description"));
  769.     descriptionLookupColumn.setColumnName("description");
  770.     descriptionLookupColumn.setResolvable(false);
  771.     descriptionLookupColumn.setCalcType(borland.jbcl.dataset.CalcType.CALC);
  772.     descriptionLookupColumn.setDataType(borland.jbcl.util.Variant.STRING);
  773.     descriptionLookupColumn.setEditable(false);
  774.     unitPriceLineItemColumn.setCaption(textRes.getString("unit_price"));
  775.     unitPriceLineItemColumn.setCurrency(true);
  776.     unitPriceLineItemColumn.setColumnName("unit_price");
  777.     unitPriceLineItemColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  778.     unitPriceLineItemColumn.setLocale(new java.util.Locale("en", "US", ""));
  779.     unitPriceLineItemColumn.setEditable(false);
  780.     extdPriceColumn.setCaption(textRes.getString("extd_price"));
  781.     extdPriceColumn.setCalcType(borland.jbcl.dataset.CalcType.CALC);
  782.     extdPriceColumn.setCurrency(true);
  783.     extdPriceColumn.setColumnName("extd_price");
  784.     extdPriceColumn.setResolvable(false);
  785.     extdPriceColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  786.     extdPriceColumn.setLocale(new java.util.Locale("en", "US", ""));
  787.     localExtdPriceColumn.setCaption(textRes.getString("local_price"));
  788.     localExtdPriceColumn.setCalcType(borland.jbcl.dataset.CalcType.CALC);
  789.     localExtdPriceColumn.setColumnName("local_extd_price");
  790.     localExtdPriceColumn.setResolvable(false);
  791.     localExtdPriceColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  792.     subtotalColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  793.     subtotalColumn.setCurrency(true);
  794.     subtotalColumn.setColumnName("subtotal");
  795.     subtotalColumn.setResolvable(false);
  796.     subtotalColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  797.     subtotalColumn.setLocale(new java.util.Locale("en", "US", ""));
  798.     localSubtotalColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  799.     localSubtotalColumn.setColumnName("local_subtotal");
  800.     localSubtotalColumn.setResolvable(false);
  801.     localSubtotalColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  802.     shippingColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  803.     shippingColumn.setCurrency(true);
  804.     shippingColumn.setColumnName("shipping");
  805.     shippingColumn.setResolvable(false);
  806.     shippingColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  807.     shippingColumn.setLocale(new java.util.Locale("en", "US", ""));
  808.     localShippingColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  809.     localShippingColumn.setColumnName("local_shipping");
  810.     localShippingColumn.setResolvable(false);
  811.     localShippingColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  812.     taxColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  813.     taxColumn.setCurrency(true);
  814.     taxColumn.setColumnName("tax");
  815.     taxColumn.setResolvable(false);
  816.     taxColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  817.     taxColumn.setLocale(new java.util.Locale("en", "US", ""));
  818.     localTaxColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  819.     localTaxColumn.setCurrency(true);
  820.     localTaxColumn.setColumnName("local_tax");
  821.     localTaxColumn.setResolvable(false);
  822.     localTaxColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  823.     totalColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  824.     totalColumn.setCurrency(true);
  825.     totalColumn.setColumnName("total");
  826.     totalColumn.setResolvable(false);
  827.     totalColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  828.     totalColumn.setLocale(new java.util.Locale("en", "US", ""));
  829.     localTotalColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  830.     localTotalColumn.setColumnName("local_total");
  831.     localTotalColumn.setResolvable(false);
  832.     localTotalColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  833.     skuColumn.setCaption(textRes.getString("sku"));
  834.     skuColumn.setColumnName("sku");
  835.     skuColumn.setDataType(borland.jbcl.util.Variant.INT);
  836.     skuColumn.setEditable(false);
  837.     productsDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\Products.txt");
  838.     unitPriceProductsColumn.setCurrency(true);
  839.     unitPriceProductsColumn.setColumnName("unit_price");
  840.     unitPriceProductsColumn.setDataType(borland.jbcl.util.Variant.DOUBLE);
  841.     unitPriceProductsColumn.setLocale(new java.util.Locale("en", "US", ""));
  842.     descriptionColumn.setColumnName("description");
  843.     descriptionColumn.setDataType(borland.jbcl.util.Variant.STRING);
  844.     productsDataSet.setDataFile(productsDataFile);
  845.     productsDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from products", null, false, false));
  846.     productsDataSet.addRowFilterListener(new AppDataModule_productsDataSet_rowFilterAdapter(this));
  847.     productColorsDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\ProductColors.txt");
  848.     productColorsDataSet.setDataFile(productColorsDataFile);
  849.     productColorsDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from productColors", null, false, false));
  850.     productSizesDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\ProductSizes.txt");
  851.     productSizesDataSet.setDataFile(productSizesDataFile);
  852.     productSizesDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from productSizes", null, false, false));
  853.     customerNoDataSet.setDataFile(customerNoDataFile);
  854.     customerNoDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from customerNo", null, false, false));
  855.     customerNoDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\CustomerNo.txt");
  856.     orderNoDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\OrderNo.txt");
  857.     orderNoDataSet.setDataFile(orderNoDataFile);
  858.     orderNoDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from orderNo ", null, false, false));
  859.     imageColumn.setColumnName("image");
  860.     imageColumn.setDataType(borland.jbcl.util.Variant.BINARY_STREAM);
  861.     imageColumn.setEditable(false);
  862.     orderNoColumn.setColumnName("order_no");
  863.     orderNoColumn.setDataType(borland.jbcl.util.Variant.INT);
  864.     orderLineItemNoColumn.setColumnName("order_no");
  865.     orderLineItemNoColumn.setDataType(borland.jbcl.util.Variant.INT);
  866.     totalQuantityColumn.setCalcType(borland.jbcl.dataset.CalcType.AGGREGATE);
  867.     totalQuantityColumn.setColumnName("total_quantity");
  868.     totalQuantityColumn.setDataType(borland.jbcl.util.Variant.INT);
  869.     totalQuantityColumn.setResolvable(false);
  870.     customerDataFile.setFileName("\\JBuilder\\samples\\borland\\samples\\intl\\application\\data\\Customers.txt");
  871.     customerDataSet.setDataFile(customerDataFile);
  872.     customerDataSet.setQuery(new borland.jbcl.dataset.QueryDescriptor(database, "select * from customers", null, false, false));
  873.     customerDataSet.addRowFilterListener(new AppDataModule_customerDataSet_rowFilterAdapter(this));
  874.     customerDataSet.addEditListener(new AppDataModule_customerDataSet_editAdapter(this));
  875.     exchangeRateDataSet.setColumns(new Column[] {exchangeRateColumn});
  876.     orderDataSet.setColumns(new Column[] {orderNoColumn, orderDateColumn, paymentMethodColumn, creditCardNoColumn, cardExpirationDateColumn});
  877.     productsDataSet.setColumns(new Column[] {unitPriceProductsColumn, descriptionColumn, imageColumn});
  878.     orderLineItemDataSet.setColumns(new Column[] {orderLineItemNoColumn, skuColumn, quantityColumn, detailsColumn, descriptionLookupColumn, unitPriceLineItemColumn, extdPriceColumn, localExtdPriceColumn, subtotalColumn, localSubtotalColumn, shippingColumn, localShippingColumn, taxColumn, localTaxColumn, totalColumn, localTotalColumn, totalQuantityColumn});
  879.     customerDataSet.setColumns(new Column[] {customerNoColumn, lastNameColumn, firstNameColumn, middleNameColumn, address1Column, address2Column, cityColumn, provinceColumn, countryColumn, emailColumn, phoneColumn, faxColumn, passwordColumn, postalCodeColumn});
  880.   }
  881.  
  882.   /**
  883.    * Return the application's single instance of AppDataModule.
  884.    * In order to ensure that the same instance of AppDataModule is
  885.    * used by all parts of the application, AppDataModule follows the
  886.    * 'singleton' object creation pattern.  Clients of the data
  887.    * module must call getDataModule() to get the single instance of
  888.    * AppDataModule.
  889.    *
  890.    * @return instance of the AppDataModule
  891.    */
  892.   static public AppDataModule getDataModule() {
  893.     // Create the instance of AppDataModule the first time
  894.     // getDataModule is called (appDataModule is initially null).
  895.     // If an instance of the data module has already been
  896.     // created, simply return it.
  897.     if (appDataModule == null) {
  898.       appDataModule = new AppDataModule();
  899.     }
  900.     return appDataModule;
  901.   }
  902.  
  903.   /**
  904.    * Specifies a JDBC data source to be used as the application's data provider.
  905.    * To use a JDBC data source instead of TextDataFile as the data
  906.    * source, setJBCDataSource must be called with the appropriate
  907.    * JDBC connection parameters before AppDataModule is instantiated.
  908.    * Note that AppDataModule is instantiated the first time
  909.    * AppDataModule.getDataModule() is called.
  910.    *
  911.    * @param URL Universal Resource Locator for a JDBC database connection
  912.    * @param user user name for a JDBC database connection
  913.    * @param password user name's password for a JDBC database connection
  914.    * @param driver JDBC driver name (e.g. 'interbase.interclient.Driver')
  915.    */
  916.   static public void setJDBCDataSource(String URL, String user, String password, String driver) {
  917.     if (appDataModule == null) {
  918.       jdbcURL = URL;
  919.       jdbcUser = user;
  920.       jdbcPassword = password;
  921.       jdbcDriver = driver;
  922.       useQueryDataSet = true;
  923.     }
  924.   }
  925.  
  926.   /**
  927.    * Returns the customerDataSet data set.
  928.    */
  929.   public borland.jbcl.dataset.QueryDataSet getCustomerDataSet() {
  930.     return customerDataSet;
  931.   }
  932.  
  933.   /**
  934.    * Returns the customerDataSetView data set.
  935.    */
  936.   public borland.jbcl.dataset.DataSetView getCustomerDataSetView() {
  937.     return customerDataSetView;
  938.   }
  939.  
  940.   /**
  941.    * Returns the exchangeRateDataSet data set.
  942.    */
  943.   public borland.jbcl.dataset.QueryDataSet getExchangeRateDataSet() {
  944.     return exchangeRateDataSet;
  945.   }
  946.  
  947.   /**
  948.    * Returns the paymentMethodDataSet data set.
  949.    */
  950.   public borland.jbcl.dataset.QueryDataSet getPaymentMethodDataSet() {
  951.     return paymentMethodDataSet;
  952.   }
  953.  
  954.   /**
  955.    * Returns the orderDataSet data set.
  956.    */
  957.   public borland.jbcl.dataset.QueryDataSet getOrderDataSet() {
  958.     return orderDataSet;
  959.   }
  960.  
  961.   /**
  962.    * Returns the orderLineItemDataSet data set.
  963.    */
  964.   public borland.jbcl.dataset.QueryDataSet getOrderLineItemDataSet() {
  965.     return orderLineItemDataSet;
  966.   }
  967.  
  968.   /**
  969.    * Returns the productsDataSet data set.
  970.    */
  971.   public borland.jbcl.dataset.QueryDataSet getProductsDataSet() {
  972.     return productsDataSet;
  973.   }
  974.  
  975.   /**
  976.    * Returns the productColorsDataSet data set.
  977.    */
  978.   public borland.jbcl.dataset.QueryDataSet getProductColorsDataSet() {
  979.     return productColorsDataSet;
  980.   }
  981.  
  982.   /**
  983.    * Returns the productSizesDataSet data set.
  984.    */
  985.   public borland.jbcl.dataset.QueryDataSet getProductSizesDataSet() {
  986.     return productSizesDataSet;
  987.   }
  988.  
  989.   /**
  990.    * Returns the customerNoDataSet data set.
  991.    */
  992.   public borland.jbcl.dataset.QueryDataSet getCustomerNoDataSet() {
  993.     return customerNoDataSet;
  994.   }
  995.  
  996.   /**
  997.    * Returns the orderNoDataSet data set.
  998.    */
  999.   public borland.jbcl.dataset.QueryDataSet getOrderNoDataSet() {
  1000.     return orderNoDataSet;
  1001.   }
  1002.  
  1003.   /**
  1004.    * Initializes newly inserted customerDataSet rows.
  1005.    * Assigns each newly inserted customer row a unique customer number and
  1006.    * fills in the country field with the localized country display
  1007.    * name for the current locale.
  1008.    *
  1009.    * @param DataSet DataSet into which a row has been inserted
  1010.    * @exception DataSetException upon invalid use of DataSet
  1011.    */
  1012.   void customerDataSet_inserted(DataSet DataSet) throws DataSetException{
  1013.     // Obtain a new, unique customer number.
  1014.     // queryCustomerNo is used as the row filtering criterion for customerDataSet
  1015.     // to restrict its scope to a single customer row.  When using a TextDataFile, queryCustomerNo is
  1016.     // also used to restrict the scope of orderDataSet to a single customer row.
  1017.     queryCustomerNo = getNextInt(customerNoDataSet, "next_customer_no");
  1018.     customerDataSet.setInt("customer_no", queryCustomerNo);
  1019.     customerDataSet.setString("country", Locale.getDefault().getDisplayCountry(Locale.getDefault()));
  1020.   }
  1021.  
  1022.   /**
  1023.    * Recalculates orderLineItemDataSet's calculated and aggregated value columns.
  1024.    *
  1025.    * @param DataSet DataSet containing changed column 
  1026.    * @param column column which has changed
  1027.    * @param variant new column value (as a Variant)
  1028.    * @exception DataSetException upon invalid use of DataSet
  1029.    */
  1030.   void exchangeRateColumn_changed(DataSet DataSet, Column column, Variant variant) throws DataSetException{
  1031.     orderLineItemDataSet.recalc();
  1032.   }
  1033.  
  1034.   /**
  1035.    * Updates the edit mask of the orderDataSet's credit_card_no column
  1036.    * to the newly selected credit card's digit pattern.
  1037.    *
  1038.    * @param DataSet DataSet containing changed column 
  1039.    * @param column column which has changed
  1040.    * @param variant new column value (as a Variant)
  1041.    * @exception DataSetException upon invalid use of DataSet
  1042.    */
  1043.   void paymentMethodColumn_changed(DataSet DataSet, Column column, Variant variant) throws DataSetException{
  1044.     // locate the credit card no pattern
  1045.     DataRow locateRow = new DataRow(paymentMethodDataSet, new String [] { "credit_card_name" });
  1046.     locateRow.setString("credit_card_name", orderDataSet.getString("payment_method"));
  1047.     paymentMethodDataSet.locate(locateRow, Locate.FIRST);
  1048.     orderDataSet.enableDataSetEvents(false);
  1049.     // set the credit card no edit mask pattern
  1050.     orderDataSet.getColumn("credit_card_no").setEditMask(paymentMethodDataSet.getString("digit_pattern"));
  1051.     // activate the new edit mask
  1052.     orderDataSet.enableDataSetEvents(true);
  1053.   }
  1054.  
  1055.   /**
  1056.    * Initializes newly inserted orderDataSet rows.
  1057.    * Assigns each newly inserted order row a unique order number.
  1058.    *
  1059.    * @param DataSet DataSet into which a row has been inserted
  1060.    * @exception DataSetException upon invalid use of DataSet
  1061.    */
  1062.   void orderDataSet_inserted(DataSet DataSet) throws DataSetException{
  1063.     DataSet.setInt("order_no", getNextInt(orderNoDataSet, "next_order_no"));
  1064.   }
  1065.  
  1066.   /**
  1067.    * Calculates the extended and localized extended prices of posted
  1068.    * orderLineItemDataSet rows.  Also sets the resource bundle lookup
  1069.    * key for localized orderLineItemDataSet descriptions
  1070.    *
  1071.    * @param readRow row of base values for calculation
  1072.    * @param dataRow row for newly calculated row values
  1073.    * @param isPosted true if readRow is a posted row
  1074.    * @exception DataSetException upon invalid use of DataSet
  1075.    */
  1076.   void orderLineItemDataSet_calcFields(ReadRow readRow, DataRow dataRow, boolean isPosted) throws DataSetException{
  1077.     if (isPosted) {
  1078.       double extdPrice = readRow.getInt("quantity") * readRow.getDouble("unit_price");
  1079.       dataRow.setDouble("extd_price", extdPrice);
  1080.       dataRow.setDouble("local_extd_price", extdPrice * exchangeRateDataSet.getDouble("exchange_rate"));
  1081.       // The item painter for the description column will lookup the
  1082.       // description for the sku in a resource bundle and display the
  1083.       // localized description
  1084.       dataRow.setString("description", "" + readRow.getInt("sku"));
  1085.     }
  1086.   }
  1087.  
  1088.   /**
  1089.    * Recalculates US and localized currency subtotal, shipping, tax,
  1090.    * and total values for the current order when a row is added to
  1091.    * orderLineItemDataSet.
  1092.    *
  1093.    * @param readRow row of base values for calculation
  1094.    * @param readWriteRow row for newly calculated values
  1095.    * @exception DataSetException upon invalid use of DataSet
  1096.    */
  1097.   void orderLineItemDataSet_calcAggAdd(ReadRow readRow, ReadWriteRow readWriteRow) throws DataSetException{
  1098.     double subtotal = readRow.getDouble("subtotal");
  1099.  
  1100.     // Calculate shipping based on number of items
  1101.     double shipping = readRow.getInt("total_quantity") * 4.95;
  1102.     readWriteRow.setDouble("shipping", shipping);
  1103.  
  1104.     // Calculate 8.5% sales tax based on sum of subtotal and shipping
  1105.     double tax = .085 * (subtotal + shipping);
  1106.     readWriteRow.setDouble("tax", tax);
  1107.  
  1108.     double total = subtotal + shipping + tax;
  1109.     readWriteRow.setDouble("total", total);
  1110.  
  1111.     // Calculate local subtotal, shipping, tax, and total based on exchange rate
  1112.     double exchangeRate = exchangeRateDataSet.getDouble("exchange_rate");
  1113.     readWriteRow.setDouble("local_subtotal", subtotal * exchangeRate);
  1114.     readWriteRow.setDouble("local_shipping", shipping * exchangeRate);
  1115.     readWriteRow.setDouble("local_tax", tax * exchangeRate);
  1116.     readWriteRow.setDouble("local_total", total * exchangeRate);
  1117.   }
  1118.  
  1119.   /**
  1120.    * Recalculates US and localized currency subtotal, shipping, tax,
  1121.    * and total values for the current order when a row is removed from
  1122.    * orderLineItemDataSet.
  1123.    *
  1124.    * @param readRow row of base values for calculation
  1125.    * @param readWriteRow row for newly calculated values
  1126.    * @exception DataSetException upon invalid use of DataSet
  1127.    */
  1128.   void orderLineItemDataSet_calcAggDelete(ReadRow readRow, ReadWriteRow readWriteRow) throws DataSetException{
  1129.     // Delegate recalculation of values to orderLineItemDataSet_calcAggAdd()
  1130.     orderLineItemDataSet_calcAggAdd(readRow, readWriteRow);
  1131.   }
  1132.  
  1133.   /**
  1134.    * Refilters rows of productsDataSet according to the current
  1135.    * productFilteringCategory.
  1136.    * productsDataSet_filterRow() is called once for each row
  1137.    * in productsDataSet when productsDataSet.open() or
  1138.    * productsDataSet.refilter() is invoked.  The value of the
  1139.    * productFilteringCategory variable is used to determine which rows
  1140.    * should be filtered into (rowFilterResponse.add()) or filtered out
  1141.    * of (rowFilterResponse.ignore()) the DataSet.  By design, product
  1142.    * categories are assigned integer values.  When the user selects a
  1143.    * new product filtering category, ProductFrame calls the data
  1144.    * module's filterProducts() method, passing it a value
  1145.    * corresponding to the desired filtering category.
  1146.    * filterProducts() assigns the new filtering category to the
  1147.    * productFilteringCategory variable and invokes
  1148.    * productsDataSet.refilter(), causing productsDataSet to be
  1149.    * refiltered adding only the desired rows.  AppDataModule's public
  1150.    * constant NO_PRODUCT_FILTER can be passed to filterProducts() to
  1151.    * filter in all rows.  
  1152.    *
  1153.    * @param readRow row to be filtered
  1154.    * @param rowFilterResponse indicates whether row should be filtered into or out of DataSet
  1155.    * @exception DataSetException upon invalid use of DataSet
  1156.    */
  1157.   void productsDataSet_filterRow(ReadRow readRow, RowFilterResponse rowFilterResponse) throws DataSetException{
  1158.     if ((productFilteringCategory == NO_PRODUCT_FILTER) ||
  1159.     (readRow.getInt("category") == productFilteringCategory)) {
  1160.       // add row to filtered DataSet
  1161.       rowFilterResponse.add();
  1162.     } else {
  1163.       // do not add row to filtered DataSet
  1164.       rowFilterResponse.ignore();
  1165.     }
  1166.   }
  1167.  
  1168.   /**
  1169.    * Sets the (single) customerDataSet filtered row to be the current
  1170.    * customerDataSetView row if the specified password is valid.
  1171.    * Also removes the unposted, new customer row from customerDataSet
  1172.    * if necessary.  Called by CustomerLookupDialog.java when user
  1173.    * presses 'OK' button to select a customer row.
  1174.    *
  1175.    * @return true if customer password is valid
  1176.    */
  1177.   public boolean isValidCustomerDataViewPassword(String password) {
  1178.     try {
  1179.       String mangledPassword = customerDataSetView.getString("mangled_password");
  1180.       if (mangledPassword.equals(PasswordMangler.manglePassword(password))) {
  1181.     // When a new order is initialized, a row is always inserted into customerDataSet
  1182.     // with a new unique customer_no value (see customerDataSet_inserted()).
  1183.     // If instead of entering a new customer, the user finds an existing customer,
  1184.     // then we first have to remove (cancel) the new customer row we added.
  1185.     customerDataSet.cancel();
  1186.     queryCustomerNo = customerDataSetView.getInt("customer_no");
  1187.     customerDataSet.refilter();
  1188.     return true;
  1189.       }
  1190.     } catch (DataSetException e) {
  1191.       DataSetException.handleException(customerDataSet, null, e, true);
  1192.     }
  1193.     return false;
  1194.   }
  1195.  
  1196.   // called by SoldToPanel.java after an existing customer has been retrieved,
  1197.   // in order to copy the customer's no into the pending order.
  1198.   public void syncCustomerNoWithCurrentOrder() {
  1199.     try {
  1200.       orderDataSet.setInt("customer_no", queryCustomerNo);
  1201.     } catch (DataSetException e) {
  1202.       DataSetException.handleException(orderDataSet, null, e, true);
  1203.     }
  1204.   }
  1205.  
  1206.   /**
  1207.    * Removes the current row from orderLineItemDataSet.
  1208.    * Called by OrderFrame.java when a user clicks the 'Remove Item'
  1209.    * button.
  1210.    */
  1211.   public void removeCurrentLineItem() {
  1212.     try {
  1213.       if (orderLineItemDataSet.getRowCount() > 0) {
  1214.     // In order to prevent users from interactively deleting rows
  1215.     // using [Ctrl][Del], we have defined a DataSet 'deleting'
  1216.     // event handler which vetoes 'deleting' events if
  1217.     // vetoDeletingEvent is true (the default value).  So in
  1218.     // order to delete an existing orderLineItem row, we need to set
  1219.     // vetoDeletingEvent false.
  1220.     vetoDeletingEvent = false;
  1221.     orderLineItemDataSet.deleteRow();
  1222.     vetoDeletingEvent = true;
  1223.       }
  1224.     } catch (DataSetException e) {
  1225.       DataSetException.handleException(orderLineItemDataSet, null, e, true);
  1226.     }
  1227.   }
  1228.  
  1229.   /**
  1230.    * Cancels the current order.
  1231.    * Called by OrderFrame.java when a user clicks the 'Cancel Order'
  1232.    * button.
  1233.    */
  1234.   public void cancelOrder() {
  1235.     StorageDataSet DataSet = null;
  1236.     try {
  1237.       // If we're using a QueryDataSet, then we can simply empty the
  1238.       // orderDataSet and orderLineItemDataSet data sets.
  1239.       if (useQueryDataSet) {
  1240.     (DataSet = orderLineItemDataSet).empty();
  1241.     // Must cancel row currently being edited before doing empty()
  1242.     (DataSet = orderDataSet).cancel();
  1243.     orderDataSet.empty();
  1244.       } else {
  1245.     // When using a TextDataFile data source, we've already loaded
  1246.     // all the orderDataSet and orderLineItemDataSet data, so we
  1247.     // have to remove the current rows we're editing in
  1248.     // orderDataSet and orderLineItemDataSet.  Delete already
  1249.     // posted line items for the current order.
  1250.     // In order to prevent users from interactively deleting rows
  1251.     // using {Ctrl-Del}, we have defined a DataSet 'deleting'
  1252.     // event handler which vetoes 'deleting' events if
  1253.     // vetoDeletingEvent is true (the default value).  So in
  1254.     // order to delete an existing orderLineItem row, we need to set
  1255.     // vetoDeletingEvent false.
  1256.     (DataSet = orderLineItemDataSet).first();
  1257.     vetoDeletingEvent = false;
  1258.     while (orderLineItemDataSet.getRowCount() > 0) {
  1259.       orderLineItemDataSet.deleteRow();
  1260.     }
  1261.     vetoDeletingEvent = true;
  1262.     (DataSet = orderDataSet).cancel();
  1263.       }
  1264.       // When using either a JDBC database or TextDataFile data source,
  1265.       // we always load in all rows of the customerDataSet, so for the
  1266.       // customerDataSet, we have to cancel the row we were currently
  1267.       // editing.  cancel() on a newly inserted row will remove the
  1268.       // row.  cancel() on a modified, existing row will undo changes
  1269.       // on the row.
  1270.       (DataSet = customerDataSet).cancel();
  1271.       // Reset the enteringOrder value to indicate we are no longer
  1272.       // entering a new order.
  1273.       enteringOrder = false;
  1274.     } catch (DataSetException e) {
  1275.       DataSetException.handleException(DataSet, null, e, true);
  1276.     }
  1277.   }
  1278.  
  1279.   /**
  1280.    * Saves the current order.
  1281.    * Called by OrderFrame.java when a user clicks on the 'Save Order'
  1282.    * button.  When using a JDBC data source, the order is immediately
  1283.    * resolved back to the database.  When using a TextDataFile data
  1284.    * source, however, changes are not resolved back to the
  1285.    * TextDataFile until resolveData() is called on application exit.
  1286.    *
  1287.    * @param password (non-mangled) password of customer submitting
  1288.    * order
  1289.    * @return false if posting any of the order rows fails (most likely due
  1290.    * to a validation error).
  1291.    */
  1292.   public boolean saveOrder(String password) {
  1293.     DataSet DataSet = null;
  1294.     try {
  1295.       // Copy summary data to master order row and post it
  1296.       DataSet = orderDataSet;
  1297.       ReadRow.copyTo(new String [] { "subtotal", "tax", "shipping", "total" },
  1298.                      orderLineItemDataSet,
  1299.              new String [] { "subtotal", "tax", "shipping_charge", "total" },
  1300.              orderDataSet);
  1301.       // In order to post a new orderDataSet row, we need to tell
  1302.       // orderDataSet's 'adding' event handler that it should not veto
  1303.       // the event.  We do that by setting vetoAddingOrUpdating false
  1304.       // when posting.
  1305.       vetoAddingOrUpdatingEvent = false;
  1306.       orderDataSet.post();
  1307.       vetoAddingOrUpdatingEvent = true;
  1308.  
  1309.       // If we added a new customer row or modified an existing one, post it
  1310.       if (isPasswordNeededToSaveOrder()) {
  1311.     customerDataSet.setString("mangled_password", PasswordMangler.manglePassword(password));
  1312.       }
  1313.       // In order to post a new or modified, existing customerDataSet
  1314.       // row, we need to tell customerDataSet's 'adding', 'updating',
  1315.       // and 'inserting' event handlers that they should not veto the
  1316.       // event.  We do that by setting vetoAddingOrUpdating and
  1317.       // vetoInsertingEvent false when posting.
  1318.       vetoAddingOrUpdatingEvent = false;
  1319.       vetoInsertingEvent = false;
  1320.       (DataSet = customerDataSet).post();
  1321.       vetoAddingOrUpdatingEvent = true;
  1322.       vetoInsertingEvent = true;
  1323.  
  1324.       // When using a query data set provider, after posting the newly
  1325.       // added order to its corresponding DataSets, we resolve it back
  1326.       // to the JDBC data source.  Note that the default query
  1327.       // resolver is smart enough to resolve only the necessary changes 
  1328.       // to the database.
  1329.       if (useQueryDataSet) {
  1330.     database.saveChanges(new DataSet [] { customerDataSet, orderDataSet, orderLineItemDataSet });
  1331.       }
  1332.       // Reset the enteringOrder value to indicate we are no longer
  1333.       // entering a new order.
  1334.       enteringOrder = false;
  1335.       return true;
  1336.     } catch (DataSetException e) {
  1337.       DataSetException.handleException(DataSet, null, e, true);
  1338.     }
  1339.     return false;
  1340.   }
  1341.  
  1342.   /**
  1343.    * Returns whether or not a password has already been given for the
  1344.    * current customerDataSet row.  Called by OrderFrame.java to
  1345.    * determine whether or not to prompt the user for a password when
  1346.    * saving an order.  Also called by AppDataModule.saveOrder() to check if a new
  1347.    * password needs to be saved with the current order.
  1348.    *
  1349.    * @return true if a password is needed 
  1350.    */
  1351.   public boolean isPasswordNeededToSaveOrder() {
  1352.     try {
  1353.       return customerDataSet.getString("mangled_password").length() == 0;
  1354.     } catch (DataSetException e) {
  1355.       DataSetException.handleException(customerDataSet, null, e, true);
  1356.     }
  1357.     return false;
  1358.   }
  1359.  
  1360.   /**
  1361.    * Resolves order data back to the appropriate data source.  
  1362.    * When using a JDBC database source, all changes will already have
  1363.    * been posted and resolved, so nothing has to be done.  When using
  1364.    * a TextDataFile, however, changes made to orderDataSet,
  1365.    * orderLineItemDataSet, and customerDataSet need to be saved back
  1366.    * to disk.  Called by WelcomeFrame.java when a user exits the
  1367.    * application.
  1368.    */
  1369.   public void resolveData() {
  1370.     DataSet DataSet = null;
  1371.     try {
  1372.       if (useQueryDataSet) {
  1373.     // No changes need to be saved because orders are posted on saveOrder()
  1374.     database.closeConnection();
  1375.       } else {
  1376.     // If enteringOrder is true at this point, then the user
  1377.     // exited without canceling or saving an order.  In this case,
  1378.     // we remove any posted detail rows in orderLineItemDataSet
  1379.     // and cancel changes in customerDataSet and orderDataSet.
  1380.     // Note that we have to remove the detail rows first, because
  1381.     // if we cancelled the master first, the details would no
  1382.     // longer have a master row, and we wouldn't be able to access
  1383.     // them using orderLineItemDataSet.
  1384.     // In order to prevent users from interactively deleting rows
  1385.     // using {Ctrl-Del}, we have defined a DataSet 'deleting'
  1386.     // event handler which vetoes 'deleting' events if
  1387.     // vetoDeletingEvent is true (the default value).  So in
  1388.     // order to delete an existing orderLineItem row, we need to set
  1389.     // vetoDeletingEvent false.
  1390.     if (enteringOrder) {
  1391.       (DataSet = orderLineItemDataSet).first();
  1392.       vetoDeletingEvent = false;
  1393.       // Delete already posted line items for the current order
  1394.       while (orderLineItemDataSet.getRowCount() > 0) {
  1395.         orderLineItemDataSet.deleteRow();
  1396.       }
  1397.       vetoDeletingEvent = true;
  1398.     }
  1399.     orderLineItemDataSet.close();
  1400.  
  1401.     if (enteringOrder) {
  1402.       // Cancel modified or newly inserted customerDataSet row
  1403.       (DataSet = customerDataSet).cancel();
  1404.     }
  1405.     customerDataSet.close();
  1406.     // Remove filter restrictions before saving.
  1407.     // TextDataFile.save() will only save rows which have been filtered
  1408.     // into the DataSet, thus in order to save back all rows we need to 
  1409.     // remove filter restrictions before saving.
  1410.     customerDataSet.removeRowFilterListener(customerDataSet.getRowFilterListener());
  1411.  
  1412.     if (enteringOrder) {
  1413.       // Cancel newly inserted orderDataSet row
  1414.       (DataSet = orderDataSet).cancel();
  1415.     }
  1416.     // Remove filter restrictions before saving.
  1417.     // TextDataFile.save() will only saves rows which have been
  1418.     // filtered into the DataSet, thus in order to save back all
  1419.     // rows we need to remove filter restrictions before saving.
  1420.     orderDataSet.close();
  1421.         orderDataSet.removeRowFilterListener(orderDataSet.getRowFilterListener());
  1422.  
  1423.     // Resolve DataSet changes back to text data file sources
  1424.     customerDataFile.save(DataSet = customerDataSet);
  1425.     orderDataFile.save(DataSet = orderDataSet);
  1426.     orderLineItemDataFile.save(DataSet = orderLineItemDataSet);
  1427.     orderNoDataFile.save(DataSet = orderNoDataSet);
  1428.     customerNoDataFile.save(DataSet = customerNoDataSet);
  1429.       }
  1430.     } catch (DataSetException e) {
  1431.       DataSetException.handleException(DataSet, null, e, true);
  1432.     } catch (Exception e) {
  1433.       borland.jbcl.util.Diagnostic.printStackTrace(e);
  1434.     }
  1435.   }
  1436.  
  1437.   /**
  1438.    * Initializes data entry rows for a new order.  Called by
  1439.    * addProductToOrderLineItem() when a product is inserted into an
  1440.    * order.  Checks whether the order has already been initialized,
  1441.    * and if not, inserts new rows in orderDataSet and customerDataSet.
  1442.    */
  1443.   public void initializeOrder() {
  1444.     DataSet DataSet = null;
  1445.     // enteringOrder tells us whether or not we have already
  1446.     // initialized the order.  Only initialize the order
  1447.     // if we haven't already done so.
  1448.     if (!enteringOrder) {
  1449.       try {
  1450.     if (useQueryDataSet) {
  1451.       // If we're using a JDBC database source, execute a query
  1452.       // which always returns an empty result.  This has the side
  1453.       // effect of loading database metadata into the DataSet so
  1454.       // that we can simply add new rows to orderDataSet and
  1455.       // orderLineItemDataSet and then post changes back with
  1456.       // database.saveChanges().  For customerDataSet, since we
  1457.       // always provide all customerDataSet rows, we use a
  1458.       // RowFilterListener to simply restrict the user to
  1459.       // navigating a single customer row.
  1460.  
  1461.       // Get the query parameters for the orderDataSet query, and
  1462.       // set them to a condition which guarantees an empty result.
  1463.       ParameterRow parameterRow = orderDataSet.getParameterRow();
  1464.       parameterRow.setInt("customer_no", INVALID_CUSTOMER);
  1465.       DataSet = orderDataSet;
  1466.  
  1467.       // Execute a query on orderDataSet which should return no
  1468.       // rows.  Because there's a MasterLinkDescriptor connecting
  1469.       // orderLineItemDataSet to orderDataSet that specifies that  
  1470.       // linked detail rows should be fetched as needed, executing
  1471.       // the query on orderDataSet also executes the query on
  1472.       // orderLineItemDataSet.
  1473.       orderDataSet.executeQuery();
  1474.  
  1475.       // Open the DataSets for subsequent DataSet operations
  1476.       orderDataSet.open();
  1477.       orderLineItemDataSet.open();
  1478.     }
  1479.     // 'inserted' event handlers on orderDataSet and
  1480.     // customerDataSet put the proper values into the newly
  1481.     // inserted rows.
  1482.  
  1483.     // To prevent a user from interactively inserting (by
  1484.     // {Ctrl-Ins}), deleting (by {Ctrl-Del}), or posting (by {PgDn}
  1485.     // or {PgUp}) our newly inserted rows, we have attached
  1486.     // 'inserting', 'adding', and 'deleting' event handlers which
  1487.     // will veto such events unless we explicitly disable them.
  1488.     // Thus in order to insert a new customer row, we need
  1489.     // to set vetoInsertingEvent false to tell our
  1490.     // customerDataSet 'inserting' event handler to allow
  1491.     // us to do an insertRow().
  1492.     (DataSet = orderDataSet).insertRow(false);
  1493.     vetoInsertingEvent = false;
  1494.     (DataSet = customerDataSet).insertRow(false);
  1495.     vetoInsertingEvent = true;
  1496.     (DataSet = orderDataSet).setInt("customer_no", customerDataSet.getInt("customer_no"));
  1497.         
  1498.     // Set enteringOrder to true so that the next time
  1499.     // initializeOrder() is called by addProductToOrderLineItem(),
  1500.     // we'll know to not reinitialize the order again.
  1501.     enteringOrder = true;
  1502.       } catch (Exception e) {
  1503.     DataSetException.handleException(DataSet, null, e, true);
  1504.       }
  1505.     }
  1506.   }
  1507.  
  1508.   /**
  1509.    * Inserts information the current row of productsDataSet into
  1510.    * orderLineItemDataSet.  Called by ProductFrame.java when a user
  1511.    * presses the "Add product to Order" button.  When inserting the
  1512.    * new order row into the orderLineItemDataSet, we check to see if
  1513.    * an identical item already exists (same sku and detail info), and
  1514.    * if so, simply increment its quantity in the order.
  1515.    */
  1516.   public void addProductToOrderLineItem() {
  1517.     try {
  1518.       // Insert and initialize new rows in orderDataSet,
  1519.       // customerDataSet, and orderLineItemDataSet if not already
  1520.       // done.
  1521.       initializeOrder();
  1522.  
  1523.       // Build a scoped DataRow (on the sku and details columns) for
  1524.       // use in locating a duplicate item already in the order.
  1525.       DataRow locateRow = new DataRow(orderLineItemDataSet, new String [] { "sku", "details" });
  1526.       locateRow.setInt("sku", productsDataSet.getInt("sku"));
  1527.       String details = getCurrentProductDefaultDetails();
  1528.       if (details.length() == 0) {
  1529.     details = "N/A";
  1530.       }
  1531.       locateRow.setString("details", details);
  1532.  
  1533.       // See if an identical order line item (same sku and details
  1534.       // column values) already exists.
  1535.       if ((orderLineItemDataSet.getRowCount() > 0) && (orderLineItemDataSet.locate(locateRow, Locate.FIRST))) {
  1536.     // If we found the same item, just increase its quantity value
  1537.     orderLineItemDataSet.setInt("quantity", orderLineItemDataSet.getInt("quantity")+1);
  1538.       } else {
  1539.     // Otherwise, insert a row for the new item.
  1540.  
  1541.     // In order to prevent users from interactively adding rows
  1542.     // using {Ctrl-Ins}, we have defined a DataSet 'inserting'
  1543.     // event handler which vetoes 'inserting' events if
  1544.     // vetoInsertingEvent is true (the default value).  So in
  1545.     // order to insert a new orderLineItem row, we need to set
  1546.     // vetoInsertingEvent to false.
  1547.     vetoInsertingEvent = false;
  1548.     orderLineItemDataSet.insertRow(false);
  1549.     vetoInsertingEvent = true;
  1550.     orderLineItemDataSet.setInt("sku", productsDataSet.getInt("sku"));
  1551.     orderLineItemDataSet.setInt("quantity", 1);
  1552.     orderLineItemDataSet.setDouble("unit_price", productsDataSet.getDouble("unit_price"));
  1553.     orderLineItemDataSet.setString("details", details);
  1554.       }
  1555.       // Post new change to update calculated aggregate values
  1556.       orderLineItemDataSet.post();
  1557.     } catch (DataSetException e) {
  1558.       DataSetException.handleException(orderLineItemDataSet, null, e, true);
  1559.     }
  1560.   }
  1561.  
  1562.   /**
  1563.    * Record new product filtering category and force refiltering to
  1564.    * occur.  Called by ProductFrame.java when a user changes the
  1565.    * current product filtering category.
  1566.    *
  1567.    * @param productFilteringCategory NO_PRODUCT_FILTER for all
  1568.    * products, 1 for software products only, 2 for deskware products
  1569.    * only, 3 for bodyware products only.  
  1570.    */
  1571.   public void filterProducts(int productFilteringCategory) {
  1572.     try {
  1573.       this.productFilteringCategory = productFilteringCategory;
  1574.       // Invoke the productsDataSet's RowFilterListener to filter rows
  1575.       // based on new filter criterion
  1576.       productsDataSet.refilter();
  1577.     } catch (Exception e) {
  1578.       DataSetException.handleException(productsDataSet, null, e, true);
  1579.     }
  1580.   }
  1581.  
  1582.   /**
  1583.    * Returns default detail info (product color/size) for the current
  1584.    * row of the productsDataSet.
  1585.    * Called by addProductToOrderLineItem() when inserting a new row.
  1586.    * 
  1587.    * @return non-localized string containing comma separated list of
  1588.    * color and size info (e.g., "white,L") or an empty string if the
  1589.    * product requires no detail information.
  1590.    */
  1591.   String getCurrentProductDefaultDetails() {
  1592.     String details = "";
  1593.     try {
  1594.       int sku = productsDataSet.getInt("sku");
  1595.  
  1596.       // Create a DataRow scoped on the sku column for doing a locate
  1597.       // within productColorsDataSet.
  1598.       DataSetView colorsView = productColorsDataSet.cloneDataSetView();
  1599.       DataRow locateRow = new DataRow(colorsView, new String [] { "sku" });
  1600.       locateRow.setInt("sku", sku);
  1601.       // Locate the first color choice for the sku
  1602.       if (colorsView.locate(locateRow, Locate.FIRST)) {
  1603.     details = colorsView.getString("available_colors");
  1604.       }
  1605.  
  1606.       // Create a DataRow scoped on the sku column for doing a locate
  1607.       // within productSizesDataSet.
  1608.       DataSetView sizesView = productSizesDataSet.cloneDataSetView();
  1609.       locateRow = new DataRow(sizesView, new String [] { "sku" });
  1610.       locateRow.setInt("sku", sku);
  1611.       // Locate the first size choice for the sku
  1612.       if (sizesView.locate(locateRow, Locate.FIRST)) {
  1613.     if (details.length() == 0) {
  1614.       details = sizesView.getString("available_sizes");
  1615.     } else {
  1616.       details += "," + sizesView.getString("available_sizes");
  1617.     }
  1618.       }
  1619.     } catch (Exception e) {
  1620.       DataSetException.handleException(productsDataSet, null, e, true);
  1621.     }
  1622.     return details;
  1623.   }
  1624.  
  1625.   /**
  1626.    * Configures data sets for browsing previous orders of the customer
  1627.    * indicated by the customerDataSet's current row.
  1628.    * Called by WelcomeFrame.java when user clicks 'View Previous
  1629.    * Orders' button, and also OrderFrame.java when user closes frame
  1630.    * after having viewed orders.
  1631.    * 
  1632.    * @param browseCustomerOrders if true, prepares data sets for
  1633.    * browsing a customer's previous orders.  
  1634.    */
  1635.   public void browseCustomerOrders(boolean browseCustomerOrders) {
  1636.     StorageDataSet DataSet = null;
  1637.     try {
  1638.       if (browseCustomerOrders) {
  1639.     // queryCustomerNo (set by CustomerLookupDialog.java's call to isValidCustomerDataViewPassword())
  1640.     // should contain contain the correct customer no 
  1641.     if (useQueryDataSet) {
  1642.       // If using a JDBC database, execute a query which selects
  1643.       // rows from orderDataSet for the selected customer.
  1644.       // Because orderLineItemDataSet's MasterLinkDescriptor
  1645.       // specifies linked detail rows should be fetched as needed,
  1646.       // executing the query on orderDataSet automatically fetches
  1647.       // the corresponding orderLineItemDataSet linked detail rows.
  1648.       ParameterRow parameterRow = orderDataSet.getParameterRow();
  1649.       parameterRow.setInt("customer_no", queryCustomerNo);
  1650.       DataSet = orderDataSet;
  1651.       orderDataSet.executeQuery();
  1652.     } else {
  1653.       // If using a TextDataFile source, force refiltering of
  1654.       // orderDataSet's rows for the selected customer.
  1655.       (DataSet = orderDataSet).refilter();
  1656.     }
  1657.     // Force updating of calculated aggregate values
  1658.     orderLineItemDataSet.recalc();
  1659.       } else {
  1660.     queryCustomerNo = INVALID_CUSTOMER;
  1661.     if (useQueryDataSet) {
  1662.       // If we're no longer browsing a customer's previous orders,
  1663.       // we can simply empty the records from the DataSet.  Note that
  1664.       // empty() discards query resolver information so that when the
  1665.       // data set is resolved (when posting a new order), records
  1666.       // emptied here will not be deleted from the server.
  1667.       (DataSet = orderDataSet).empty();
  1668.       (DataSet = orderLineItemDataSet).empty();
  1669.     } else {
  1670.       // If we're using a TextDataFile data source, refilter orderDataSet
  1671.       // for no order rows.
  1672.       (DataSet = orderDataSet).refilter();
  1673.     }
  1674.       }
  1675.     } catch (Exception e) {
  1676.       DataSetException.handleException(DataSet, null, e, true);
  1677.     }
  1678.   }
  1679.  
  1680.   /**
  1681.    * Restricts navigation of orderDataSet to rows for the current customer (stored in queryCustomerNo).
  1682.    *
  1683.    * @param readRow row to be filtered
  1684.    * @param rowFilterResponse indicates whether row should be filtered into or out of DataSet
  1685.    * @exception DataSetException upon invalid use of DataSet
  1686.    */
  1687.   void orderDataSet_filterRow(ReadRow readRow, RowFilterResponse rowFilterResponse) throws DataSetException{
  1688.     if (readRow.getInt("customer_no") == queryCustomerNo) {
  1689.       // Add row to filtered DataSet
  1690.       rowFilterResponse.add();
  1691.     } else {
  1692.       // Do not add row to filtered DataSet
  1693.       rowFilterResponse.ignore();
  1694.     }
  1695.   }
  1696.  
  1697.   /**
  1698.    * Sets the current row of the exchangeRateDataSet to the current locale's country.
  1699.    * If a country can't be found, the current row is set to the row for "US".
  1700.    * Localized calculations are performed based upon the conversion rate taken from the
  1701.    * current row of exchangeRateDataSet.
  1702.    */
  1703.   private void readExchangeRate() {
  1704.     try {
  1705.       // Build a scoped DataRow (on the country_code column) for
  1706.       // use in locating a country code.
  1707.       DataRow locateRow = new DataRow(exchangeRateDataSet, new String [] { "country_code" });
  1708.  
  1709.       locateRow.setString("country_code", Locale.getDefault().getCountry());
  1710.       // If the country exists in exchangeRateDataSet, the following locate() should
  1711.       // position the current row of exchangeRateDataSet to that row.
  1712.       if (!exchangeRateDataSet.locate(locateRow, Locate.FIRST)) {
  1713.     // If we couldn't find the country, set the rate to the US rate.
  1714.     locateRow.setString("country_code", "US");
  1715.     exchangeRateDataSet.locate(locateRow, Locate.FIRST);
  1716.       }
  1717.     } catch (Exception e) {
  1718.       DataSetException.handleException(exchangeRateDataSet, null, e, true);
  1719.     }
  1720.   }
  1721.  
  1722.   /**
  1723.    * Returns an array of strings containing detail choices (sizes or
  1724.    * colors) for a product.  Called by
  1725.    * ResourceableLineItemDetailEditor.java to get a list of sizes or
  1726.    * colors to present as details for a product item.  The value in
  1727.    * the keyColumnName column of the current row of keyDataSet should
  1728.    * contain a string value which can be used as the lookup key for
  1729.    * rows in lookupDataSet.  For example, if the sku (keyColumnName)
  1730.    * column of the current row of productsDataSet (keyDataSet)
  1731.    * contains the value '1000', all values from the color
  1732.    * (lookupColumnName) column for rows whose sku (keyColumnName)
  1733.    * column value in productColorsDataSet (lookupDataSet) are '1000'
  1734.    * will be returned.
  1735.    *
  1736.    * @param keyDataSet DataSet whose current row's keyColumnName
  1737.    * column value contains a lookup key
  1738.    * @param keyColumnName name of column in keyDataSet containing
  1739.    * lookup key value
  1740.    * @param lookupDataSet DataSet which may contain rows matching a
  1741.    * lookup key
  1742.    * @param lookupColumnName name of column in lookupDataSet from
  1743.    * which to extract string values 
  1744.    * @return array of strings with matched lookup values from
  1745.    * lookupColumnName
  1746.    */
  1747.   String [] getLookupChoices(DataSet keyDataSet, String keyColumnName, DataSet lookupDataSet, String lookupColumnName) {
  1748.     String [] choices = new String[0];
  1749.     try {
  1750.       Array array = new Array();
  1751.       DataRow locateRow = new DataRow(lookupDataSet, new String [] { keyColumnName });
  1752.       Variant locateVariant = new Variant();
  1753.       keyDataSet.getVariant(keyColumnName, locateVariant);
  1754.       locateRow.setVariant(keyColumnName, locateVariant);
  1755.       if (lookupDataSet.locate(locateRow, Locate.FIRST)) {
  1756.     Variant foundVariant = new Variant();
  1757.     do {
  1758.       lookupDataSet.getVariant(lookupColumnName, foundVariant);
  1759.       array.add(foundVariant.toString());
  1760.     } while (lookupDataSet.locate(locateRow, Locate.NEXT));
  1761.       }
  1762.       choices = new String[array.size()];
  1763.       array.copyTo(choices);
  1764.     } catch (Exception e) {
  1765.       DataSetException.handleException(lookupDataSet, null, e, true);
  1766.     }
  1767.     return choices;
  1768.   }
  1769.  
  1770.   /**
  1771.    * Increments an integer value the specified column of a DataSet.
  1772.    * Called by the DataSet 'inserted' event handlers for
  1773.    * customerDataSet and orderDataSet to get unique customer and order
  1774.    * numbers, respectively.  When using a JDBC database, assumes that
  1775.    * a query has already been defined for the DataSet which provides
  1776.    * the row to be incremented.  After the value is incremented, it is
  1777.    * immediately posted back to the server.
  1778.    *
  1779.    * @param DataSet name of DataSet containing integer columnName
  1780.    * @param columnName column name of DataSet containing integer value
  1781.    * to be incremented
  1782.    * @return incremented integer value
  1783.    */
  1784.   int getNextInt(QueryDataSet DataSet, String columnName) throws DataSetException{
  1785.     if (useQueryDataSet) {
  1786.       DataSet.executeQuery();
  1787.     }
  1788.     DataSet.open();
  1789.     int nextInt = DataSet.getInt(columnName);
  1790.     DataSet.setInt(columnName, nextInt + 1);
  1791.     DataSet.post();
  1792.     if (useQueryDataSet) {
  1793.       DataSet.saveChanges(DataSet);
  1794.     }
  1795.     return nextInt;
  1796.   }
  1797.  
  1798.   /**
  1799.    * Updates localized resources for newly selected locale.
  1800.    * Required for implementation of LocaleChangeListener interface.
  1801.    * Note that DataSet.enableDataSetEvents() must be called in order
  1802.    * for newly set column properties to take effect.
  1803.    * 
  1804.    * @param e LocaleChangeEvent providing information about new locale 
  1805.    */
  1806.   public void localeChanged(LocaleChangeEvent e) {
  1807.     try {
  1808.       // Get resource bundles for new locale
  1809.       textRes = ResourceBundle.getBundle("borland.samples.intl.application.resources.textRes", e.getLocale());
  1810.  
  1811.       // Set new localized captions
  1812.       customerDataSet.enableDataSetEvents(false);
  1813.       customerDataSet.getColumn("last_name").setCaption(textRes.getString("last_name"));
  1814.       customerDataSet.getColumn("middle_name").setCaption(textRes.getString("middle_name"));
  1815.       customerDataSet.getColumn("first_name").setCaption(textRes.getString("first_name"));
  1816.       customerDataSet.getColumn("address1").setCaption(textRes.getString("address1"));
  1817.       customerDataSet.getColumn("address2").setCaption(textRes.getString("address2"));
  1818.       customerDataSet.getColumn("city").setCaption(textRes.getString("city"));
  1819.       customerDataSet.getColumn("province").setCaption(textRes.getString("province"));
  1820.       customerDataSet.getColumn("country").setCaption(textRes.getString("country"));
  1821.       customerDataSet.getColumn("e_mail").setCaption(textRes.getString("e_mail"));
  1822.       customerDataSet.getColumn("phone").setCaption(textRes.getString("phone"));
  1823.       customerDataSet.getColumn("fax").setCaption(textRes.getString("fax"));
  1824.       customerDataSet.getColumn("postal_code").setCaption(textRes.getString("postal_code"));
  1825.  
  1826.       // Set new localized edit masks
  1827.       customerDataSet.getColumn("postal_code").setEditMask(textRes.getString("postal_code_editmask"));
  1828.       customerDataSet.getColumn("phone").setEditMask(textRes.getString("phone_editmask"));
  1829.       customerDataSet.getColumn("fax").setEditMask(textRes.getString("phone_editmask"));
  1830.       customerDataSet.getColumn("province").setEditMask(textRes.getString("province_editmask"));
  1831.       customerDataSet.enableDataSetEvents(true);
  1832.  
  1833.       // Set new localized captions
  1834.       orderLineItemDataSet.getColumn("quantity").setCaption(textRes.getString("quantity"));
  1835.       orderLineItemDataSet.getColumn("details").setCaption(textRes.getString("details"));
  1836.       orderLineItemDataSet.getColumn("description").setCaption(textRes.getString("description"));
  1837.       orderLineItemDataSet.getColumn("unit_price").setCaption(textRes.getString("unit_price"));
  1838.       orderLineItemDataSet.getColumn("extd_price").setCaption(textRes.getString("extd_price"));
  1839.       orderLineItemDataSet.getColumn("local_extd_price").setCaption(textRes.getString("local_price"));
  1840.       orderLineItemDataSet.getColumn("sku").setCaption(textRes.getString("sku"));
  1841.  
  1842.       // Set new localized edit and display masks
  1843.       orderDataSet.enableDataSetEvents(false);
  1844.       orderDataSet.getColumn("card_expiration_date").setEditMask(textRes.getString("month_year_editmask"));
  1845.       orderDataSet.getColumn("order_date").setDisplayMask(((SimpleDateFormat) SimpleDateFormat.getDateInstance(DateFormat.DEFAULT, e.getLocale())).toPattern());
  1846.       orderDataSet.getColumn("card_expiration_date").setDisplayMask(textRes.getString("month_year_editmask"));
  1847.       orderDataSet.enableDataSetEvents(true);
  1848.  
  1849.       orderLineItemDataSet.enableDataSetEvents(false);
  1850.       orderLineItemDataSet.getColumn("local_extd_price").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(e.getLocale())).toPattern());
  1851.       orderLineItemDataSet.getColumn("local_subtotal").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(e.getLocale())).toPattern());
  1852.       orderLineItemDataSet.getColumn("local_shipping").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(e.getLocale())).toPattern());
  1853.       orderLineItemDataSet.getColumn("local_tax").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(e.getLocale())).toPattern());
  1854.       orderLineItemDataSet.getColumn("local_total").setDisplayMask(((DecimalFormat) DecimalFormat.getCurrencyInstance(e.getLocale())).toPattern());
  1855.       orderLineItemDataSet.enableDataSetEvents(true);
  1856.  
  1857.       // Set new localized 'value required' string
  1858.       String requiredString = textRes.getString("value_required");
  1859.       lastNameRequiredPainter.setDisplayString(requiredString);
  1860.       lastNameRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1861.       firstNameRequiredPainter.setDisplayString(requiredString);
  1862.       firstNameRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1863.       address1RequiredPainter.setDisplayString(requiredString);
  1864.       address1RequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1865.       cityRequiredPainter.setDisplayString(requiredString);
  1866.       cityRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1867.       provinceRequiredPainter.setDisplayString(requiredString);
  1868.       provinceRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1869.       countryRequiredPainter.setDisplayString(requiredString);
  1870.       countryRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1871.       paymentMethodRequiredPainter.setDisplayString(requiredString);
  1872.       paymentMethodRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1873.       creditCardNoRequiredPainter.setDisplayString(requiredString);
  1874.       creditCardNoRequiredPainter.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1875.       cardExpirationDateRequiredPainter.setDisplayString(requiredString);
  1876.       cardExpirationDateColumn.setItemPainter(new FocusableItemPainter(new SelectableTextItemPainter()));
  1877.  
  1878.       // Set new resource bundle for localized description lookup painter
  1879.       descriptionLookupItemPainter.setResourceBundle(textRes);
  1880.       descriptionLookupItemPainter.setTextItemPainter(new SelectableTextItemPainter());
  1881.       orderLineItemDataSet.getColumn("description").setItemPainter(new FocusableItemPainter(descriptionLookupItemPainter));
  1882.  
  1883.       // Set new resource bundle for detail item editor and painter
  1884.       orderLineItemDetailEditor.setResourceBundle(textRes);
  1885.       orderLineItemDetailPainter.setResourceBundle(textRes);
  1886.  
  1887.       // When the locale changes, we need to get localized description data for the new locale.
  1888.       // Note that by using productsDataSetView here instead of productsDataSet, we don't need
  1889.       // to save and restore our current cursor position within the productsDataSet, and the
  1890.       // ProductsFrame will only refresh its currently displayed row (since it's using
  1891.       // productsDataSet as its data set)
  1892.       DataSetView productsDataSetView = productsDataSet.cloneDataSetView();
  1893.       productsDataSetView.open();
  1894.       productsDataSetView.first();
  1895.  
  1896.       // Add image and localized description data
  1897.       while (productsDataSetView.inBounds()) {
  1898.     int sku = productsDataSetView.getInt("sku");
  1899.     String localizedDescription = textRes.getString("" + sku);
  1900.     productsDataSetView.setString("description", localizedDescription);
  1901.     productsDataSetView.next();
  1902.       }
  1903.       productsDataSetView.post();
  1904.       productsDataSetView.close();
  1905.  
  1906.       // Update exchangeRateDataSet to point to new country's exchange rate
  1907.       readExchangeRate();
  1908.       // Force calc fields to be recalculated based on new exchange rate values
  1909.       orderLineItemDataSet.recalc();
  1910.  
  1911.     } catch (Exception ex) {
  1912.       borland.jbcl.util.Diagnostic.printStackTrace(ex);
  1913.     }
  1914.   }
  1915.  
  1916.   /**
  1917.    * Forces calculated aggregates to be updated immediately by posting
  1918.    * a row when its quantity value changes.  Note that aggregated
  1919.    * calculations are only performed on posted records.  Normal
  1920.    * calculated columns, in constrast, can be updated immediately when
  1921.    * the quantity value is 'put' and do not require the row to be
  1922.    * posted.
  1923.    *
  1924.    * @param DataSet DataSet containing changed column 
  1925.    * @param column column which has changed
  1926.    * @param variant new column value (as a Variant)
  1927.    * @exception DataSetException upon invalid use of DataSet
  1928.    */
  1929.   void quantityColumn_changed(DataSet DataSet, Column column, Variant variant) throws DataSetException{
  1930.     orderLineItemDataSet.post();
  1931.   }
  1932.  
  1933.   /**
  1934.    * Filters all but the current customer number row (stored in
  1935.    * queryCustomerNo) out of customerDataSet.
  1936.    *
  1937.    * @param readRow row to be filtered
  1938.    * @param rowFilterResponse indicates whether row should be filtered into or out of DataSet
  1939.    * @exception DataSetException upon invalid use of DataSet
  1940.    */
  1941.   void customerDataSet_filterRow(ReadRow readRow, RowFilterResponse rowFilterResponse) throws DataSetException{
  1942.     if (readRow.getInt("customer_no") == queryCustomerNo) {
  1943.       // Add row to filtered DataSet
  1944.       rowFilterResponse.add();
  1945.     } else {
  1946.       // Do not add row to filtered DataSet
  1947.       rowFilterResponse.ignore();
  1948.     }
  1949.   }
  1950.  
  1951.   /**
  1952.    * Prevents a user from interactively deleting customer rows, by
  1953.    * throwing a VetoException() if the event occurs and
  1954.    * vetoDeletingEvent is 'true'.  Called if the user presses
  1955.    * {Ctrl-Del} from within a FieldControl attached to
  1956.    * customerDataSet.
  1957.    *
  1958.    * @param DataSet DataSet from which a row is being deleted
  1959.    * @exception Exception (VetoException) to prevent the row from being deleted
  1960.    */
  1961.   void customerDataSet_deleting(DataSet DataSet) throws Exception{
  1962.     if (vetoDeletingEvent) {
  1963.       throw new VetoException("<ignore>");
  1964.     }
  1965.   }
  1966.  
  1967.   /**
  1968.    * Prevents a user from interactively moving off a customer row and
  1969.    * thereby posting it by throwing a VetoException() if the event
  1970.    * occurs and vetoAddingOrUpdatingEvent is 'true'.
  1971.    *
  1972.    * Called if the user moves off a newly inserted customerDataSet
  1973.    * row by pressing [PgUp] or [PgDn] from within a FieldControl
  1974.    * attached to customerDataSet.
  1975.    *
  1976.    * @param DataSet DataSet into which a row is being added
  1977.    * @param readWriteRow new row being added
  1978.    * @exception Exception (VetoException) to prevent the row from being added
  1979.    */
  1980.   void customerDataSet_adding(DataSet DataSet, ReadWriteRow readWriteRow) throws Exception{
  1981.     if (vetoAddingOrUpdatingEvent) {
  1982.       throw new VetoException("<ignore>");
  1983.     }
  1984.   }
  1985.  
  1986.   /**
  1987.    * Prevents a user from interactively moving off a customer row and
  1988.    * thereby posting it by throwing a VetoException() if the event
  1989.    * occurs and vetoAddingOrUpdatingEvent is 'true'.
  1990.    * Called if the user moves off a modified, existing
  1991.    * customerDataSet row by pressing [PgUp] or [PgDn] from within a
  1992.    * FieldControl attached to customerDataSet.
  1993.    *
  1994.    * @param DataSet DataSet in which a row is being updated
  1995.    * @param readWriteRow modified row after update
  1996.    * @param readRow original row before update
  1997.    * @exception Exception (VetoException) to prevent the row from being updated
  1998.    */
  1999.   void customerDataSet_updating(DataSet DataSet, ReadWriteRow readWriteRow, ReadRow readRow) throws Exception{
  2000.     if (vetoAddingOrUpdatingEvent) {
  2001.       throw new VetoException("<ignore>");
  2002.     }
  2003.   }
  2004.  
  2005.   /**
  2006.    * Prevents a user from interactively inserting any new order line
  2007.    * item rows by throwing a VetoException() if the event occurs and
  2008.    * vetoInsertingEvent is 'true'.  Called if the user presses
  2009.    * [Ctrl][Ins] before editing an existing customer row from within
  2010.    * OrderFrame.java.
  2011.    *
  2012.    * @param DataSet DataSet into which a row is being inserted
  2013.    * @param readWriteRow new row being inserted
  2014.    * @exception Exception (VetoException) to prevent the row from being inserted
  2015.    */
  2016.   void customerDataSet_inserting(DataSet DataSet) throws Exception{
  2017.     if (vetoInsertingEvent) {
  2018.       throw new VetoException("<ignore>");
  2019.     }
  2020.   }
  2021.  
  2022.   /**
  2023.    * Prevents a user from interactively deleting any order rows by
  2024.    * throwing a VetoException() if the event occurs and vetoDeletingEvent
  2025.    * is 'true'.
  2026.    * Called if the user presses {Ctrl-Del} from within
  2027.    * a FieldControl attached to orderDataSet.
  2028.    *
  2029.    * @param DataSet DataSet from which a row is being deleted
  2030.    * @exception Exception (VetoException) to prevent the row from being deleted
  2031.    */
  2032.   void orderDataSet_deleting(DataSet DataSet) throws Exception{
  2033.     if (vetoDeletingEvent) {
  2034.       throw new VetoException("<ignore>");
  2035.     }
  2036.   }
  2037.  
  2038.   /**
  2039.    * Prevents a user from interactively moving off a customer row and
  2040.    * thereby posting it by throwing a VetoException() if the event
  2041.    * occurs and vetoAddingOrUpdatingEvent is 'true'.  Called if the
  2042.    * user moves off a newly inserted orderDataSet row by pressing
  2043.    * [PgUp] or [PgDn] from within a FieldControl attached to
  2044.    * orderDataSet.
  2045.    *
  2046.    * @param DataSet DataSet into which a row is being added
  2047.    * @param readWriteRow new row being added
  2048.    * @exception Exception (VetoException) to prevent the row from being added
  2049.    */
  2050.   void orderDataSet_adding(DataSet DataSet, ReadWriteRow readWriteRow) throws Exception{
  2051.     if (vetoAddingOrUpdatingEvent) {
  2052.       throw new VetoException("<ignore>");
  2053.     }
  2054.   }
  2055.  
  2056.   /**
  2057.    * Prevents a user from interactively deleting any order line item
  2058.    * rows by throwing a VetoException() if the event occurs and
  2059.    * vetoDeletingEvent is 'true'.
  2060.    * Called if the user presses {Ctrl-Del} from within a GridControl
  2061.    * attached to orderLineItemDataSet.
  2062.    *
  2063.    * @param DataSet DataSet from which a row is being deleted
  2064.    * @exception Exception (VetoException) to prevent the row from being deleted
  2065.    */
  2066.   void orderLineItemDataSet_deleting(DataSet DataSet) throws Exception{
  2067.     if (vetoDeletingEvent) {
  2068.       throw new VetoException("<ignore>");
  2069.     }
  2070.   }
  2071.  
  2072.   /**
  2073.    * Prevents a user from interactively inserting any new order line
  2074.    * item rows by throwing a VetoException() if the event occurs and
  2075.    * vetoInsertingEvent is 'true'.  Called if the user presses
  2076.    * [Ctrl][Ins] from within the GridControl attached to
  2077.    * orderLineItemDataSet.
  2078.    *
  2079.    * @param DataSet DataSet into which a row is being inserted
  2080.    * @param readWriteRow new row being inserted
  2081.    * @exception Exception (VetoException) to prevent the row from being inserted
  2082.    */
  2083.   void orderLineItemDataSet_inserting(DataSet DataSet) throws Exception{
  2084.     if (vetoInsertingEvent) {
  2085.       throw new VetoException("<ignore>");
  2086.     }
  2087.   }
  2088.  
  2089. }
  2090.  
  2091. // The following adapter classes were created by the UI Designer to hook
  2092. // up DataSet events to the appropriate methods in AppDataModule.
  2093.  
  2094. class AppDataModule_customerDataSet_editAdapter extends borland.jbcl.dataset.EditAdapter {
  2095.   AppDataModule adaptee;
  2096.  
  2097.   AppDataModule_customerDataSet_editAdapter(AppDataModule adaptee) {
  2098.     this.adaptee = adaptee;
  2099.   }
  2100.  
  2101.   public void inserted(DataSet DataSet) throws DataSetException{
  2102.     adaptee.customerDataSet_inserted(DataSet);
  2103.   }
  2104.  
  2105.   public void deleting(DataSet DataSet) throws Exception{
  2106.     adaptee.customerDataSet_deleting(DataSet);
  2107.   }
  2108.  
  2109.   public void adding(DataSet DataSet, ReadWriteRow readWriteRow) throws Exception{
  2110.     adaptee.customerDataSet_adding(DataSet, readWriteRow);
  2111.   }
  2112.  
  2113.   public void inserting(DataSet DataSet) throws Exception{
  2114.     adaptee.customerDataSet_inserting(DataSet);
  2115.   }
  2116.  
  2117.   public void updating(DataSet DataSet, ReadWriteRow readWriteRow, ReadRow readRow) throws Exception{
  2118.     adaptee.customerDataSet_updating(DataSet, readWriteRow, readRow);
  2119.   }
  2120.  
  2121. }
  2122.  
  2123. class AppDataModule_exchangeRateColumn_columnChangeAdapter extends borland.jbcl.dataset.ColumnChangeAdapter {
  2124.   AppDataModule adaptee;
  2125.  
  2126.   AppDataModule_exchangeRateColumn_columnChangeAdapter(AppDataModule adaptee) {
  2127.     this.adaptee = adaptee;
  2128.   }
  2129.  
  2130.   public void changed(DataSet DataSet, Column column, Variant variant) throws DataSetException{
  2131.     adaptee.exchangeRateColumn_changed(DataSet, column, variant);
  2132.   }
  2133. }
  2134.  
  2135. class AppDataModule_paymentMethodColumn_columnChangeAdapter extends borland.jbcl.dataset.ColumnChangeAdapter {
  2136.   AppDataModule adaptee;
  2137.  
  2138.   AppDataModule_paymentMethodColumn_columnChangeAdapter(AppDataModule adaptee) {
  2139.     this.adaptee = adaptee;
  2140.   }
  2141.  
  2142.   public void changed(DataSet DataSet, Column column, Variant variant) throws DataSetException{
  2143.     adaptee.paymentMethodColumn_changed(DataSet, column, variant);
  2144.   }
  2145. }
  2146.  
  2147. class AppDataModule_orderDataSet_editAdapter extends borland.jbcl.dataset.EditAdapter {
  2148.   AppDataModule adaptee;
  2149.  
  2150.   AppDataModule_orderDataSet_editAdapter(AppDataModule adaptee) {
  2151.     this.adaptee = adaptee;
  2152.   }
  2153.  
  2154.   public void inserted(DataSet DataSet) throws DataSetException{
  2155.     adaptee.orderDataSet_inserted(DataSet);
  2156.   }
  2157.  
  2158.   public void deleting(DataSet DataSet) throws Exception{
  2159.     adaptee.orderDataSet_deleting(DataSet);
  2160.   }
  2161.  
  2162.   public void adding(DataSet DataSet, ReadWriteRow readWriteRow) throws Exception{
  2163.     adaptee.orderDataSet_adding(DataSet, readWriteRow);
  2164.   }
  2165.  
  2166. }
  2167.  
  2168. class AppDataModule_orderLineItemDataSet_calcFieldsAdapter implements borland.jbcl.dataset.CalcFieldsListener {
  2169.   AppDataModule adaptee;
  2170.  
  2171.   AppDataModule_orderLineItemDataSet_calcFieldsAdapter(AppDataModule adaptee) {
  2172.     this.adaptee = adaptee;
  2173.   }
  2174.  
  2175.   public void calcFields(ReadRow readRow, DataRow dataRow, boolean boolean1) throws DataSetException{
  2176.     adaptee.orderLineItemDataSet_calcFields(readRow, dataRow, boolean1);
  2177.   }
  2178. }
  2179.  
  2180. class AppDataModule_orderLineItemDataSet_calcAggFieldsAdapter extends borland.jbcl.dataset.CalcAggFieldsAdapter {
  2181.   AppDataModule adaptee;
  2182.  
  2183.   AppDataModule_orderLineItemDataSet_calcAggFieldsAdapter(AppDataModule adaptee) {
  2184.     this.adaptee = adaptee;
  2185.   }
  2186.  
  2187.   public void calcAggAdd(ReadRow readRow, ReadWriteRow readWriteRow) throws DataSetException{
  2188.     adaptee.orderLineItemDataSet_calcAggAdd(readRow, readWriteRow);
  2189.   }
  2190.  
  2191.   public void calcAggDelete(ReadRow readRow, ReadWriteRow readWriteRow) throws DataSetException{
  2192.     adaptee.orderLineItemDataSet_calcAggDelete(readRow, readWriteRow);
  2193.   }
  2194.  
  2195. }
  2196.  
  2197. class AppDataModule_productsDataSet_rowFilterAdapter implements borland.jbcl.dataset.RowFilterListener {
  2198.   AppDataModule adaptee;
  2199.  
  2200.   AppDataModule_productsDataSet_rowFilterAdapter(AppDataModule adaptee) {
  2201.     this.adaptee = adaptee;
  2202.   }
  2203.  
  2204.   public void filterRow(ReadRow readRow, RowFilterResponse rowFilterResponse) throws DataSetException{
  2205.     adaptee.productsDataSet_filterRow(readRow, rowFilterResponse);
  2206.   }
  2207. }
  2208.  
  2209. class AppDataModule_quantityColumn_columnChangeAdapter extends borland.jbcl.dataset.ColumnChangeAdapter {
  2210.   AppDataModule adaptee;
  2211.  
  2212.   AppDataModule_quantityColumn_columnChangeAdapter(AppDataModule adaptee) {
  2213.     this.adaptee = adaptee;
  2214.   }
  2215.  
  2216.   public void changed(DataSet DataSet, Column column, Variant variant) throws DataSetException{
  2217.     adaptee.quantityColumn_changed(DataSet, column, variant);
  2218.   }
  2219. }
  2220.  
  2221.  
  2222. class AppDataModule_orderDataSet_rowFilterAdapter implements borland.jbcl.dataset.RowFilterListener {
  2223.   AppDataModule adaptee;
  2224.  
  2225.   AppDataModule_orderDataSet_rowFilterAdapter(AppDataModule adaptee) {
  2226.     this.adaptee = adaptee;
  2227.   }
  2228.  
  2229.   public void filterRow(ReadRow readRow, RowFilterResponse rowFilterResponse) throws DataSetException{
  2230.     adaptee.orderDataSet_filterRow(readRow, rowFilterResponse);
  2231.   }
  2232. }
  2233.  
  2234.  
  2235. class AppDataModule_customerDataSet_rowFilterAdapter implements borland.jbcl.dataset.RowFilterListener {
  2236.   AppDataModule adaptee;
  2237.  
  2238.   AppDataModule_customerDataSet_rowFilterAdapter(AppDataModule adaptee) {
  2239.     this.adaptee = adaptee;
  2240.   }
  2241.  
  2242.   public void filterRow(ReadRow readRow, RowFilterResponse rowFilterResponse) throws DataSetException{
  2243.     adaptee.customerDataSet_filterRow(readRow, rowFilterResponse);
  2244.   }
  2245. }
  2246.  
  2247. class AppDataModule_orderLineItemDataSet_editAdapter extends borland.jbcl.dataset.EditAdapter {
  2248.   AppDataModule adaptee;
  2249.  
  2250.   AppDataModule_orderLineItemDataSet_editAdapter(AppDataModule adaptee) {
  2251.     this.adaptee = adaptee;
  2252.   }
  2253.  
  2254.   public void deleting(DataSet DataSet) throws Exception{
  2255.     adaptee.orderLineItemDataSet_deleting(DataSet);
  2256.   }
  2257.  
  2258.   public void inserting(DataSet DataSet) throws Exception{
  2259.     adaptee.orderLineItemDataSet_inserting(DataSet);
  2260.   }
  2261.  
  2262. }
  2263.  
  2264.