![]() ![]() ![]() |
9
Sample Programs
The Sample Database
The DemoData Program
Base Example
BaseButton Example
Cell Validation Example
Row Validation Example
Exception Message Example
Popup Menu Example
9.1 The Sample Database
The sample database that is included with JClass DataSource has the structure shown in Figure 92. This diagram shows the table names, column (field) names, and data types. Many-one relationships are shown by terminating the dotted lines connecting two tables with a black circle at the "many" end of the relationship. The key fields are shown at the top of each table and the foreign keys are designated by placing the tag "(FK)" after the data type.
Figure 82 : Entity-Relationship diagram for the sample database.
9.2 The DemoData Program
We'll begin with an example of using a class called
DemoData
, used to retrieve data from a database, and then show how aHiGrid
is used to display selected columns from the database.The class performs these functions:
What follows is a line-by-line breakdown of the code. Lines 1-20 are the standard copyright notice that accompanies all JClass examples. They should be assumed as the beginning lines of every other example given in this chapter. Lines 22-29 list the package name and the libraries that
DemoData
imports. This package identifies itself as part of the examples that accompany the product. Packagedatasource
forms the data source part of JClass HiGrid's code. As this example shows, it is responsible for setting up the connection to the chosen database and then passing the appropriate SQL query to the database. Actually, thecom.klg.jclass.datasource.jdbc
package is where the code to connect via JDBC resides (or to an ODBC, through a JDBC-ODBC bridge).Lines 54-57 define the constants that are used to specify which is the desired database connection. Line 59 states that the Microsoft Access database is currently selected.
Line 62 is the beginning of the code for the constructor. It sets up a String for the JDBC-ODBC driver, then embeds the database connection attempt in a
jdbc:<subprotocol>:<subname>try
block. Line 74 sets up a newDataTableConnection
. The JDBC URL structure is defined generally as follows:In this line,
jdbc
is the standard base,subprotocol
is the particular data source type, andsubname
is an additional specification that the subprotocol uses. In our example, the subprotocol isodbc
. The Driver Manager uses the subprotocol to match the proper driver to a specific subprotocol. The subname identifies the name of the data source.Line 74 begins the process of instantiating a new connection. Line 75 declares the driver. In fact, lines 74-79 are a concrete instance of a constructor call whose general form is
com.klg.jclass.datasource.jdbc.DataTableConnection(String driver, String url, String user, String password, String database)
. Parameterdriver
is a String indicating which driver to load,url
is the URL String described above,user
is the String for the user's name,password
is a String for the user's password, if required, anddatabase
is the String for the database name, which may be null. This class defines various ways of connecting to databases, such as using a host name and port, or anodbc
style connection, in addition to the one used in our example. Once the connection is established, a query sets up the structure for the data that will be retrieved.In line 108 of our example, the top-level table of our grid is declared in a query specifying that the database table,
Orders
, is to be used. We wish to include, as sub-tables, information contained in tables Customers,
Territories,
OrderDetailsand Products-Categories. The last-mentioned is a detail level consisting of a join of two tables.
Line 108 shows that the
MetaData
class holds the structure of the query. Two constructors are used. First, the "root" constructor is called to set up and execute the query to bootstrap root levels of theDataModel
and theMetaDataModel
. This constructor executes the query and sets the resultingDataTable
as the root of theDataTableTree
. Call this constructor first, then call theMetaData(DataModel dataModel, DataTableConnection ds_connection, MetaData parent)
constructor to build the meta data tree hierarchy. Next, the second form of the constructor is called to add master-detail relationships. All of this is accomplished in lines 113-125.Note that the class' constructor does all the work, and a try block encloses all of the code. If the class can't be instantiated, the exception will print an error message on the monitor.
Once an instance of this class is successfully created, we have established a connection to the named database and the query will return a result set.
Joins are accomplished programmatically by code such as is seen in lines 116 and 124. They may be specified by using Bean customizers if you are using an IDE.
Lines 127 and following show how to attach virtual columns to a grid. These use the
BaseVirtualColumn
class as illustrated in line 151, 156, and 160. The type of aggregation to be done is specified usingBaseVirtualColumn
constants, as shown in lines 154, 158, and 162.Finally, commit policies for each level are set, beginning at line 188. All three commit policies are illustrated.
1 /**
2 * Copyright (c) 2002, QUEST SOFTWARE. All Rights Reserved.
3 * http://www.quest.com
4 *
5 * This file is provided for demonstration and educational uses only.
6 * Permission to use, copy, modify and distribute this file for
7 * any purpose and without fee is hereby granted, provided that the
8 * above copyright notice and this permission notice appear in all
9 * copies, and that the name of Quest not be used in
10 * advertising or publicity pertaining to this material without the
11 * specific, prior written permission of an authorized representative
12 * of Quest.
13 *
14 * QUEST SOFTWARE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
15 * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
16 * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. QUEST
18 * SOFTWARE WILL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY USERS AS A
19 * RESULT OF USING MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
20 * DERIVATIVES.
21 */
22 package examples.datasource.jdbc;
23
24 import com.klg.jclass.datasource.BaseVirtualColumn;
25 import com.klg.jclass.datasource.MetaDataModel;
26
27 import com.klg.jclass.datasource.TreeData;
28 import com.klg.jclass.datasource.jdbc.DataTableConnection;
29 import com.klg.JClass DataSource.jdbc.MetaData;
30
31 /**
32 * This is an implementation of the JClass DataSource DataModel which
33 * relies on the our own JDBC wrappers (rather than IDE-specific data
34 * binding).
35 *
36 * It models a database for a fictitious bicycle company. The same
37 * schema has been implemented using an MS Access database
38 * and a SQLAnywhere database (demo.mdb and demo.db respectively).
39 * They contain the same table structures and data.
40 *
41 * The default is to use the jdbc-odbc bridge to connect to the Access
42 * implementation of the data base. You can change which data base is
43 * accessed by changing the dataBase variable to either SA or SYB below.
44 *
45 * This is the tree hierarchy for the data:
46 * Orders
47 * Customers
48 * Territory
49 * OrderDetails
50 * Products-Categories
51 *
52 */
53 public class DemoData extends TreeData {
54
55 public static final int MS = 1;
56 public static final int SA = 2;
57 public static final int SYB = 3;
58 //Change the definition of database to any of the above constants.
59 int dataBase = MS;
60 DataTableConnection c;
61
62 public DemoData() {
63 String driver = "sun.jdbc.odbc.JdbcOdbcDriver";
64 if (System.getProperty("java.vendor").indexOf("Microsoft") != -1) {
65 // use the driver that Microsoft Internet Explorer wants
66 driver = "com.ms.jdbc.odbc.JdbcOdbcDriver";
67 }
68 try {
69 switch (dataBase) {
70 case MS:
71 // This connection uses the jdbc-odbc bridge to
72 // connect to the Access implementation of the
73 // data base.
74 c = new DataTableConnection(
75 driver, // driver
76 "jdbc:odbc:JClassDemo", // url
77 "Admin", // user
78 "", // password
79 null); // database
80 break;
81
82 // This connection uses the jdbc-odbc bridge to connect
83 // to the SQLAnywhere implementation of the data base.
84 case SA:
85 c = new DataTableConnection(
86 "sun.jdbc.odbc.JdbcOdbcDriver", // driver
87 "jdbc:odbc:JClassDemoSQLAnywhere",// url
88 "dba", // user
89 "sql", // password
90 null); // database
91 break;
92
93 // This connection uses Sybase's jConnect type 4
94 // driver to connect to the SQLAnywhere implementation
95 // of the data base.
96 case SYB:
97 c = new DataTableConnection(
98 "com.sybase.jdbc.SybDriver", // driver
99 "jdbc:sybase:Tds:localhost:1498", // url
100 "dba", // user
101 "sql", // password
102 "HiGridDemoSQLAnywhere"); // database
103 break;
104 default:
105 System.out.println("No database chosen");
106 }
107
108 // Create the Orders MetaData
109 MetaData Orders = new MetaData(this, c, " select * from Orders order by OrderID asc");
110 Orders.setDescription("Orders");
111
112 // Create the Customer MetaData
113 MetaData Customers = new MetaData(this, Orders, c);
114 Customers.setDescription("Customers");
115 Customers.setStatement("select * from Customers where CustomerID = ?");
116 Customers.joinOnParentColumn("CustomerID","CustomerID");
117 Customers.open();
118
119 // Create the Territory MetaData
120 MetaData Territory = new MetaData(this, Customers, c);
121 Territory.setDescription("Territory");
122 String t = "select TerritoryID, TerritoryName from Territories where TerritoryID = ?";
123 Territory.setStatement(t);
124 Territory.joinOnParentColumn("TerritoryID","TerritoryID");
125 Territory.open();
126
127 // Create the OrderDetails MetaData
128 // Three virtual columns are used:
129 //
130 // TotalLessTax (Quantity * UnitPrice)
131 // SalesTax (TotalLessTax * TaxRate) and
132 // LineTotal (TotalLessTax + SalesTax).
133 //
134 // Thus, when Quantity and/or UnitPrice is changed, these derived
135 // values reflect the changes immediately.
136 // Note 1: TaxRate is not a real column either, it is a
137 // constant returned by the sql statement.
138 // Note 2: Virtual columns can themselves be used to derive other
139 // virtual columns. They are evaluated from left to right.
140 MetaData OrderDetails = new MetaData(this, Orders, c);
141 OrderDetails.setDescription("OrderDetails");
142 String detail_query = "select OrderDetailID, OrderID, ProductID, ";
143 detail_query += " DateSold, Quantity, UnitPrice, ";
144 detail_query += " '0.15' AS TaxRate ";
145 detail_query += " from OrderDetails where OrderID = ?";
146 OrderDetails.setStatement(detail_query);
147 OrderDetails.joinOnParentColumn("OrderID","OrderID");
148 OrderDetails.open();
149
150 //Extend the row with some calculated values.
151 BaseVirtualColumn TotalLessTax = new BaseVirtualColumn(
152 "TotalLessTax",
153 java.sql.Types.FLOAT,
154 BaseVirtualColumn.PRODUCT,
155 new String[] {"Quantity", "UnitPrice"});
156 BaseVirtualColumn SalesTax = new BaseVirtualColumn(
157 "SalesTax",java.sql.Types.FLOAT,
158 BaseVirtualColumn.PRODUCT,
159 new String[] {"TotalLessTax", "TaxRate"});
160 BaseVirtualColumn LineTotal = new BaseVirtualColumn(
161 "LineTotal",java.sql.Types.FLOAT,
162 BaseVirtualColumn.SUM,
163 new String[] {"TotalLessTax", "SalesTax"});
164
165 OrderDetails.addColumn(TotalLessTax);
166 OrderDetails.addColumn(SalesTax);
167 OrderDetails.addColumn(LineTotal);
168
169 // Create the Products MetaData
170 MetaData Products = new MetaData(this, OrderDetails, c);
171 Products.setDescription("Products");
172 String query = "select a.ProductID, a.ProductDescription,a.ProductName,";
173 query += " a.CategoryID, a.UnitPrice, a.Picture, ";
174 query += " b.CategoryName";
175 query += " from Products a, Categories b";
176 query += " where a.ProductID = ?";
177 query += " and a.CategoryID = b.CategoryID";
178 Products.setStatement(query);
179 Products.joinOnParentColumn("ProductID","ProductID");
180 Products.open();
181
182 // Override the table-column associations for the Products table
183 // to exclude the Picture column so it is not included as part of
184 // the update. Precision problems cause the server to think it's
185 // changed.
186 Products.setColumnTableRelations("Products", new String[] {"ProductID", "ProductDescription", "ProductName", "CategoryID", "UnitPrice"});
187
188 // Override the default commit policy COMMIT_LEAVING_ANCESTOR
189 Orders.setCommitPolicy(MetaDataModel.COMMIT_LEAVING_RECORD);
190 OrderDetails.setCommitPolicy(MetaDataModel.COMMIT_LEAVING_ANCESTOR);
191 Customers.setCommitPolicy(MetaDataModel.COMMIT_LEAVING_ANCESTOR);
192 Products.setCommitPolicy(MetaDataModel.COMMIT_MANUALLY);
193 Territory.setCommitPolicy(MetaDataModel.COMMIT_LEAVING_ANCESTOR);
194
195 } catch (Exception e) {
196 System.out.println("DemoData failed to initialize " + e.toString());
197 }
198 }
199
200 }
9.3 Base Example
The
DemoData
database connection is used for many of the examples that illustrate the use of JClass HiGrid. The first example is calledBaseExample
. It shows a basic grid and sets up a general framework of displaying messages above the grid to describe the particular operation currently being demonstrated in the example. It is worthwhile to describe the structure of the messaging framework, since it is reused in other examples.
BaseExample
performs the following tasks:
- Sets up panels in a
JCExitFrame
.- Defines methods for setting a title and a prompt.
- Defines a method called
countChars()
that is used to count the number of new lines in the passed-in prompt. The method is used in the many examples that are subclassed fromBaseExample
.- Instantiates a grid and sets the data model with a meta data structure defined by
DemoData.
- Uses
HiGridFormatNodeListener
to randomly color all cells.Messages have a title, a prompt, and a message area that contains text found in the variable standardText. The size of the message area is determined with the help of a method called
countChars
. After defining the Strings for the title, prompt, and standard text,setPrompt
is called. It usescountChars
to determine if the text area should be reduced from its maximum size, then displays the text.Pre-JClass 4.0 technique for traversing the format tree:
Methods
void setRandomRowBackgroundColor(boolean recordFormat) {setRandomRowBackgroundColor
set the colors of the different types of rows in the grid, but more generally, illustrate how the grid'sFormatTree
is accessed and used to navigate from the root on down. Note the use of theTreeIterator
class, which acts as a specializedEnumeration
type to allow traversal of the format tree.
FormatTree formatTree = grid.getFormatTree();
FormatNode node = (FormatNode) formatTree.getRoot();
setRandomRowBackgroundColor(recordFormat, node);
}
void setRandomRowBackgroundColor(boolean recordFormat, FormatNode node) {
if (node == null) {
return;
}
Color randomColor = new Color(lightColor(), lightColor(),
lightColor());
if (recordFormat) {
setRowBackgroundColor(node.getRecordFormat(), randomColor);
}
else {
setRowBackgroundColor(node.getFooterFormat(), randomColor);
setRowBackgroundColor(node.getBeforeDetailsFormat(), randomColor);
setRowBackgroundColor(node.getAfterDetailsFormat(), randomColor);
}
TreeIterator ti = node.getIterator();
while (ti.hasMoreElements()) {
node = (FormatNode)ti.get();
setRandomRowBackgroundColor(recordFormat, node);
ti.nextElement();
}
}The call to
lightColor
returns a random value that is used to specify the RGB color value of the row.Simpler JClass 6.0 and higher technique:
class BaseExampleHiGridFormatNodeListener extends
HiGrid's
event delegation model lets you catch the creation of each format node as it is about to happen so that you can change the default plaf color to one that is randomly chosen.
HiGridFormatNodeAdapter {
public void beforeCreateFormatNodeContents(HiGridFormatNodeEvent event) {
CellStyleModel cellStyle = grid.getRecordCellStyle();
Color randomColor = new Color(lightColor(), lightColor(), lightColor());
cellStyle.setBackground(randomColor);
}Turning off repainting
Whenever a grid is first loaded, it's a good idea to turn off repainting. Therefore, a common code idiom is:
grid.setBatched(true);
if (applet == null) {
grid.setDataModel(newjclass
.datasource.examples.jdbc.DemoData());
if (grid.getDataModel() == null) {
grid.setDataModel(
newjclass
.datasource.examples.vector.VectorData());
}
}
else {
grid.setDataModel(newjclass
.datasource.examples.vector.VectorData());
}
setRandomRowBackgroundColor(true);
grid.setBatched(false);The operations that could cause repaint flickers are bracketed by
setBatched(true)
...setBatched(false)
.
9.4 BaseButton Example
This example merely adds a button to the bottom of the
public class BaseButtonExample extends BaseExampleBaseExample
's frame. It extendsBaseExample
and its button responds toactionPerformed
events.
implements ActionListener {The button is used in subsequent examples to initiate changes to the grid.
9.5 Cell Validation Example
The interface for doing cell-level validation is illustrated here.
public class CellValidationExample extends BaseExample implements
HiGridValidateListenerBecause this class implements the
HiGridListener
interface, it must define methodsstateIsInvalid
,valueChangedBegin
, andvalueChangedEnd
. The class also uses the validation capabilities ofjclass.cell
to fire astateIsInvalid
message if the validation criteria are not met. If the change meets the validation criteria, a message on the standard output reports the changed information. If not, the application refuses to change the current row until valid data is placed in the cell being edited.Here is how
/*valueChangedBegin
validates the cell's contents:
* HiGridValidateListener implementation
*/
/**
* Invoked just before the data source value of the field is updated.
*/
public void valueChangedBegin(HiGridValidateEvent e) {
JCValidateEvent event = e.getValidateEvent();
RowNode rowNode = e.getRowNode();
// find out the user-defined name for this level
//(assume it is unique)
String levelName = rowNode.getDataTableModel().getMetaData().getDescription();
String oldValue = getStringValue(event.getOldValue());
String newValue = getStringValue(event.getValue());
// reject any changes where the new entry is empty
if (newValue.length() == 0) {
event.setValid(false);
return;
}
// only do validation for top-level PurchaseOrderNumber column
if (levelName.compareTo("Orders") == 0 &&
e.getColumn().compareTo("PurchaseOrderNumber") == 0) {
// reject any changes where the first character changes
boolean valid = (oldValue.length() > 0) &&
(newValue.length() > 0) &&
(oldValue.charAt(0) == newValue.charAt(0));
event.setValid(valid);
}
}Method
setValid
determines whetherstateIsInvalid
will be fired or not.
9.6 Row Validation Example
There are times when you must simultaneously validate more than a single cell. There may be additional dependencies between the cells in a row that must be checked before committing changes to the database. The current example is based on the premise that an item cannot be delivered before it is ordered.
package examples.higrid.validation;
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
import com.klg.jclass.datasource.*;
import com.klg.jclass.higrid.*;
import com.klg.jclass.util.swing.JCMessageHelper;
import com.klg.jclass.util.swing.JCExitFrame;
import examples.higrid.BaseExample;
/**
* Do row level validation in HiGrid.
*/
public class RowValidationExample extends BaseExample {
public RowValidationExample() {
super();
grid.getDataModel().addDataModelListener(
new RowValidateDataModelListener());
setTitle("Row Validation");
setPrompt(usage);
}
private Frame myFrame;
public void setFrame(Frame f) {
myFrame = f;
}
static String usage = "This example shows how to do row level
validation.\n" +
"It will reject changes where the RequiredDate comes before
the OrderDate.";
void validateRow(DataModelEvent event, RowNode rowNode) {
// find out the user-defined name for this level (assume it is
// unique)
DataTableModel model = rowNode.getDataTableModel();
String levelName = model.getMetaData().getDescription();
// only do validation for top-level columns
if (levelName.compareTo("Orders") == 0) {
long bookmark = rowNode.getBookmark();
// ensure that RequiredDate is after OrderDate
try {
Date orderDate = (Date)model.getResultData(bookmark,
"OrderDate");
Date requiredDate = (Date)model.getResultData(bookmark,
"RequiredDate");
if (orderDate.after(requiredDate)) {
event.cancelProposedAction();
JCMessageHelper.showError("Row Validation",
"OrderDate must come before
RequiredDate");
}
}
catch (Exception e) {
}
}
}
class RowValidateDataModelListener extends DataModelAdapter {
public void beforeMoveToCurrentRow(DataModelEvent e) {
validateRow(e, grid.getCurrentRowNode());
}
public void beforeCommitRow(DataModelEvent e) {
validateRow(e, grid.getRowTree().findRecordRowNode(null,
e.getBookmark()));
}
}
public static void main(String args[]) throws InterruptedException {
JCExitFrame f = new JCExitFrame("Row Validation Example");
RowValidationExample app = new RowValidationExample();
app.setFrame(f);
f.getContentPane().add(app);
f.pack();
f.show();
javax.swing.FocusManager.getCurrentManager().focusNextComponent(
app.getGrid());
}
}
9.7 Exception Message Example
The
examples.higrid.exception.ExceptionMessageExample.java
lists all the HiGrid and DataSource event constants defined incom.klg.jclass.higrid.HiGridEvent
andcom.klg.jclass.datasource.DataModelEvent
. It presents two ways of handling events. By default, HiGrid presents event and exception messages in a dialog window. You can provide your own mechanism by implementing the HiGrid listener interface and providing code there to deal with the event. Similarly, to deal withDataModel
events, you implement theDataModelListener
interface.To use your own event handling routine, have your class implement the
grid.addHiGridListener(this);HiGridListener
interface and register itself as aHiGridListener
:
grid.getErrorHandler().setShowErrorDialog(false);Customized event handling is illustrated by displaying the type of event in the text area using the messaging mechanism defined in Section 9.3, Base Example.
9.8 Popup Menu Example
JClass HiGrid's popup menu can be changed, or completely replaced by one of your own design. The example shows that the short and long forms of the popup menu can be selected using the constants
EditPopupMenu.DEFAULT_SHORT_POPUPMENU_LIST
andEditPopupMenu.DEFAULT_LONG_POPUPMENU_LIST
. It goes on to show how to install additional choices; specifically, an "about" choice that, when clicked, presents a message in the applet's text area.To view the code, see examples.higrid.menu.PopupMenuExample.java.
![]() ![]() ![]() |