JavaGram Agile Development Sharam Hekmat

JavaGram
Agile Development
Version 4
Sharam Hekmat
PragSoft Corporation
Copyright © 2009-2012 by Sharam Hekmat, PragSoft Corporation
All rights reserved.
Printed in the USA by PragSoft Press.
To download JavaGram and code samples included in this book, visit www.pragsoft.com
To send feedback to the author (bug reports, enhancement suggestions, and the like) write
to: [email protected]
JavaGram is free for non-commercial use. To enquire about commercial use and licensing
of JavaGram, or formal training, write to: [email protected]
Contents
1
INTRODUCTION .............................................................................................................................. 12
1.1 BACKGROUND ..................................................................................................................................12
1.1.1
Agility Criteria .......................................................................................................................13
1.1.2
Barriers to Rapid Development .............................................................................................14
1.1.3
Barriers to Rapid Testing ......................................................................................................15
1.1.4
Barriers to Rapid Evolution ...................................................................................................16
1.2 SALIENT FEATURES..........................................................................................................................16
1.2.1
Server Centricity ....................................................................................................................17
1.2.2
Browser-based and Native Desktop Clients ..........................................................................19
1.2.3
Static and Dynamic Loading..................................................................................................19
1.2.4
Code Caching ........................................................................................................................19
1.2.5
Built-in Types .........................................................................................................................20
1.2.6
Object Orientation .................................................................................................................20
1.2.7
Multiple Inheritance ..............................................................................................................21
1.2.8
Automatic Remoting...............................................................................................................21
1.2.9
Asynchronous Method Invocation..........................................................................................22
1.2.10
Declarative GUIs ..............................................................................................................22
1.2.11
Parameterized Text ...........................................................................................................22
1.2.12
Database Interaction ........................................................................................................23
1.2.13
Serialization and Parsing .................................................................................................23
1.2.14
Business Objects ...............................................................................................................23
1.2.15
Java Interoperation ...........................................................................................................23
1.3 IMPLEMENTATION ............................................................................................................................24
1.3.1
JavaGram Runtime Environment...........................................................................................24
1.3.2
Compilation ...........................................................................................................................25
1.3.3
JavaGram IDE .......................................................................................................................26
1.3.4
JavaGram Server Monitor .....................................................................................................26
1.3.5
JavaGram Standard Library Scripts ......................................................................................26
1.4 DOWNLOAD AND INSTALLATION .....................................................................................................27
2
FUNDAMENTALS ............................................................................................................................ 28
2.1 EXAMPLE .........................................................................................................................................28
2.2 LOADING SCRIPTS ............................................................................................................................30
2.3 EXPRESSIONS AND STATEMENTS......................................................................................................34
2.4 TYPES ..............................................................................................................................................34
2.5 CONTROL FLOW ...............................................................................................................................35
2.6 ITERATION .......................................................................................................................................39
2.7 COMPOSITES ....................................................................................................................................42
2.7.1
Containers .............................................................................................................................42
2.7.2
Lists........................................................................................................................................43
2.7.3
Vectors ...................................................................................................................................44
2.7.4
Maps ......................................................................................................................................47
2.7.5
Object Literals .......................................................................................................................50
2.7.6
Literal versus Dynamic ..........................................................................................................52
2.8 EXCEPTION HANDLING ....................................................................................................................53
3
OBJECT-ORIENTED PROGRAMMING ...................................................................................... 56
3.1
3.2
3.3
3.4
Content
INHERITANCE ...................................................................................................................................56
SHOPPING CART EXAMPLE...............................................................................................................58
MUTUAL CLASSES ...........................................................................................................................67
FINAL QUALIFIER .............................................................................................................................68
3
3.5
3.6
3.7
4
THIS AND SUPER ..............................................................................................................................68
METHOD PARAMETERS ....................................................................................................................69
CLASS VARIABLES ...........................................................................................................................71
GUI PROGRAMMING ..................................................................................................................... 72
4.1 DEMO APPLICATION ........................................................................................................................72
4.2 PANELS, LAYOUTS, AND FIELDS ......................................................................................................74
4.2.1
Data Binding..........................................................................................................................80
4.3 TREES ..............................................................................................................................................82
4.3.1
Using a Tree Model ...............................................................................................................85
4.4 TABLES ............................................................................................................................................88
4.4.1
Using a Table Model .............................................................................................................90
4.4.2
Lists........................................................................................................................................93
4.5 GRIDS ..............................................................................................................................................95
4.6 COMPONENT STATUS .....................................................................................................................102
4.7 DIALOGS AND ALERTS ...................................................................................................................107
4.7.1
Dialogs ................................................................................................................................107
4.7.2
Alerts....................................................................................................................................109
4.7.3
File Chooser ........................................................................................................................110
4.7.4
Color Chooser .....................................................................................................................111
4.8 GRAPHS .........................................................................................................................................112
4.8.1
Pie Charts ............................................................................................................................112
4.8.2
Line Graphs .........................................................................................................................114
4.8.3
Bar and Stack Graphs ..........................................................................................................115
4.8.4
Bubble Graph ......................................................................................................................117
4.8.5
Pipe Graph ..........................................................................................................................119
4.8.6
Gauge and Range Bar .........................................................................................................120
4.8.7
Flow Graphs ........................................................................................................................121
4.9 MENUS AND TOOLBARS .................................................................................................................125
4.9.1
Pull-down Menus .................................................................................................................125
4.9.2
Popup Menus .......................................................................................................................128
4.9.3
Toolbars ...............................................................................................................................128
4.10
GOOGLE MAP ............................................................................................................................130
4.11
CODE EDITOR ............................................................................................................................132
4.12
WORKING WITH HTML.............................................................................................................134
4.12.1
Displaying HTML ...........................................................................................................134
4.12.2
Generating HTML Reports .............................................................................................136
4.12.3
Generating Images ..........................................................................................................137
4.13
TIPS AND TRICKS.......................................................................................................................137
4.13.1
Using Boiler Plates .........................................................................................................137
4.13.2
Managing Component Visibility .....................................................................................138
4.13.3
Correct Use of Layouts ...................................................................................................139
4.13.4
Managing Periodic Tasks ...............................................................................................140
4.13.5
Using Worker Threads ....................................................................................................141
4.13.6
Procedural Creation of Components ..............................................................................143
4.14
BROWSER-BASED GUIS.............................................................................................................144
4.15
SUMMARY OF ELEMENTS ..........................................................................................................145
5
SQL PROGRAMMING .................................................................................................................. 147
5.1 WORKING WITH DATABASES .........................................................................................................147
5.1.1
Explicit Connection .............................................................................................................148
5.1.2
Implicit Connection .............................................................................................................148
5.2 TEXT MEMBERS .............................................................................................................................150
5.2.1
Text ......................................................................................................................................150
5.2.2
Text.sql.................................................................................................................................151
4
JavaGram Agile Development
5.3 TRANSACTIONS ..............................................................................................................................152
5.3.1
Creating a Database ............................................................................................................152
5.3.2
Creating Tables ...................................................................................................................152
5.3.3
Dropping Tables ..................................................................................................................154
5.3.4
Inserting Rows .....................................................................................................................154
5.3.5
Updating Rows ....................................................................................................................156
5.3.6
Deleting Rows ......................................................................................................................157
5.4 QUERIES.........................................................................................................................................157
5.4.1
Retrieving Rows ...................................................................................................................158
5.4.2
Processing a Result Set ........................................................................................................159
5.4.3
Retrieving Attributes ............................................................................................................159
5.4.4
Counting Rows .....................................................................................................................160
5.4.5
Performing Joins .................................................................................................................160
5.5 CALLING STORED PROCEDURES .....................................................................................................162
5.6 STREAMLINED DATA MODELING ...................................................................................................163
5.7 BUSINESS OBJECT MODEL .............................................................................................................165
5.7.1
Subclassing Object...............................................................................................................165
5.7.2
File Handling .......................................................................................................................168
5.7.3
Locking ................................................................................................................................173
6
ADVANCED TOPICS ..................................................................................................................... 179
6.1 CLIENT-SERVER COMMUNICATION ................................................................................................179
6.1.1
Remote Methods ..................................................................................................................180
6.1.2
Remote Classes ....................................................................................................................185
6.1.3
Exception Handling .............................................................................................................189
6.1.4
Targeted Remote Calls ........................................................................................................189
6.1.5
Clocal, slocal, and side ........................................................................................................191
6.2 THREADS .......................................................................................................................................192
6.2.1
Working with Threads..........................................................................................................192
6.2.2
Synchronization ...................................................................................................................194
6.2.3
ThreadLocal Fields..............................................................................................................195
6.2.4
Timer Class ..........................................................................................................................196
6.3 ASYNCHRONOUS BEHAVIOR ..........................................................................................................197
6.3.1
Local Asynchronous Call .....................................................................................................198
6.3.2
Parallel Processing .............................................................................................................199
6.3.3
Remote Asynchronous Call ..................................................................................................200
6.3.4
Guarded Asynchronous Call................................................................................................201
6.4 REPORT GENERATION ....................................................................................................................201
6.4.1
Report Class ........................................................................................................................203
6.4.2
ReportEngine Class .............................................................................................................203
6.4.3
FopConverter Class .............................................................................................................204
6.4.4
JodConverter Class .............................................................................................................206
6.4.5
Example ...............................................................................................................................207
6.5 WEB SERVICES...............................................................................................................................213
6.5.1
Architecture .........................................................................................................................213
6.5.2
JavaGramService .................................................................................................................213
6.5.2.1
6.5.2.2
6.5.2.3
JavaGramService Methods ............................................................................................................ 214
XML Syntax for methodCall() ...................................................................................................... 215
XML Representation of JavaGram Data ....................................................................................... 217
6.5.3
Software Installation ............................................................................................................218
EDIT TOMCAT’S .......................................................................................................................................219
6.5.4
Configuration.......................................................................................................................220
6.5.4.1
6.5.4.2
6.5.4.3
6.5.4.4
Content
Server Configuration ..................................................................................................................... 220
JAGWS Configuration .................................................................................................................. 221
Additional JAR files ...................................................................................................................... 221
Virtual Directory ........................................................................................................................... 222
5
6.5.5
Virtual Directory .................................................................................................................222
6.5.6
Client Example ....................................................................................................................223
6.6 EFFICIENCY CONSIDERATIONS .......................................................................................................225
6.6.1
System-level Caching ...........................................................................................................225
6.6.2
System-level Compression ...................................................................................................225
6.6.3
Application-level Caching ...................................................................................................226
6.6.4
Remoting Performance Factors ...........................................................................................226
7
DEPLOYMENT ............................................................................................................................... 228
7.1 RUNNING A PROGRAM ...................................................................................................................228
7.1.1
JavaGram Options ...............................................................................................................228
7.1.2
Java Options ........................................................................................................................231
7.1.3
Compilation .........................................................................................................................231
7.2 SERVER DEPLOYMENT ...................................................................................................................232
7.2.1
Application Server ...............................................................................................................232
7.2.2
Proxy Server ........................................................................................................................237
7.2.3
Multi-tier Servers .................................................................................................................238
7.2.4
Deploying a Server as a Service ..........................................................................................239
7.3 NATIVE CLIENT DEPLOYMENT .......................................................................................................239
7.3.1
JavaGram Application Booter .............................................................................................239
7.3.2
Automatic Upgrade..............................................................................................................241
7.4 BROWSER CLIENT DEPLOYMENT ...................................................................................................242
7.4.1
HTML Embedding ...............................................................................................................242
7.4.2
Flash Security Sandbox .......................................................................................................244
7.4.3
Partitions .............................................................................................................................244
7.4.4
Deploying to a Web Server ..................................................................................................246
7.4.5
Server Deployment with BlazeDS ........................................................................................246
7.5 STANDALONE DEPLOYMENT ..........................................................................................................247
7.6 FILE CACHING ................................................................................................................................248
7.6.1
Server-side Caching.............................................................................................................248
7.6.2
Client-side Caching .............................................................................................................248
7.7 JAVAGRAM SERVER MONITOR ......................................................................................................249
7.7.1
Running JSM........................................................................................................................249
7.7.2
Server Tabs ..........................................................................................................................250
8
QUEST SAMPLE APPLICATION................................................................................................ 256
8.1 INTRODUCTION ..............................................................................................................................256
8.1.1
Requirements .......................................................................................................................256
8.1.1.1
8.1.1.2
8.1.1.3
8.1.1.4
8.1.1.5
8.1.1.6
8.1.2
User Interface ......................................................................................................................260
8.1.2.1
8.1.2.2
8.1.2.3
8.1.2.4
8.1.2.5
8.1.2.6
8.1.2.7
8.1.2.8
8.1.2.9
8.1.2.10
8.1.2.11
8.2
6
Issue Life Cycle ............................................................................................................................. 257
Issue BO ........................................................................................................................................ 258
History BO .................................................................................................................................... 259
User BO......................................................................................................................................... 259
Attachment BO .............................................................................................................................. 259
Rules BO ....................................................................................................................................... 260
Login Window............................................................................................................................... 260
Main Window................................................................................................................................ 261
Menubar and Toolbar .................................................................................................................... 262
User Admin Panel ......................................................................................................................... 263
User Details Panel ......................................................................................................................... 263
Issue Search Panel ......................................................................................................................... 264
Issue Details Panel ........................................................................................................................ 266
Generating Reports ........................................................................................................................ 268
Log Panel ...................................................................................................................................... 269
Pick Lists Panel ........................................................................................................................ 269
Help Panel ................................................................................................................................ 270
DESIGN ..........................................................................................................................................270
JavaGram Agile Development
8.2.1
Object Model .......................................................................................................................270
8.2.1.1
8.2.1.2
8.2.1.3
8.2.1.4
8.2.1.5
8.2.1.6
8.2.1.7
quest/ ............................................................................................................................................. 270
quest/gui/ ....................................................................................................................................... 271
quest/bom/ ..................................................................................................................................... 272
quest/gui/user/ ............................................................................................................................... 273
quest/gui/issue/ .............................................................................................................................. 274
quest/gui/misc/ .............................................................................................................................. 275
quest/gui/setting/ ........................................................................................................................... 275
8.2.2
Data Model ..........................................................................................................................276
8.3 IMPLEMENTATION ..........................................................................................................................276
8.3.1
Business Objects ..................................................................................................................276
8.3.1.1
8.3.1.2
8.3.1.3
8.3.1.4
8.3.1.5
8.3.1.6
8.3.2
Main Window .......................................................................................................................290
8.3.2.1
8.3.2.2
8.3.2.3
8.3.2.4
8.3.3
9
PickListPanel................................................................................................................................. 322
LogPanel ....................................................................................................................................... 323
Miscellaneous ......................................................................................................................324
8.3.6.1
8.3.6.2
8.3.7
IssueSearch .................................................................................................................................... 306
IssueScreen .................................................................................................................................... 309
AssignDialog ................................................................................................................................. 319
ResolveDialog ............................................................................................................................... 320
RejectDialog .................................................................................................................................. 321
Setting ..................................................................................................................................322
8.3.5.1
8.3.5.2
8.3.6
UserSearch .................................................................................................................................... 301
UserScreen .................................................................................................................................... 303
PasswordDialog............................................................................................................................. 304
Issue Management ...............................................................................................................306
8.3.4.1
8.3.4.2
8.3.4.3
8.3.4.4
8.3.4.5
8.3.5
Quest ............................................................................................................................................. 290
AppPanel ....................................................................................................................................... 293
AppTree ........................................................................................................................................ 295
Commands..................................................................................................................................... 298
User Admin ..........................................................................................................................301
8.3.3.1
8.3.3.2
8.3.3.3
8.3.4
User BO......................................................................................................................................... 276
Issue BO ........................................................................................................................................ 280
Attachment BO .............................................................................................................................. 284
History BO .................................................................................................................................... 287
Rule BO......................................................................................................................................... 288
DbTables ....................................................................................................................................... 289
AboutDialog .................................................................................................................................. 324
HtmlPanel ...................................................................................................................................... 324
Configuration.......................................................................................................................324
EXTENSIONS .................................................................................................................................. 326
9.1 INTRODUCTION ..............................................................................................................................326
9.1.1
Parse Tree............................................................................................................................326
9.1.2
Types ....................................................................................................................................328
9.1.3
Objects .................................................................................................................................329
9.1.4
IO .........................................................................................................................................330
9.1.5
Environment.........................................................................................................................330
9.2 WRITING A GUI EXTENSION ..........................................................................................................330
9.2.1
Usage ...................................................................................................................................331
9.2.2
Naming Conventions ............................................................................................................332
9.2.3
Implementation ....................................................................................................................332
9.3 WRITING A TEXT EXTENSION.........................................................................................................336
9.4 WRITING AN AD-HOC EXTENSION..................................................................................................338
9.5 PACKAGING YOUR EXTENSION ......................................................................................................340
10
CORE REFERENCE ...................................................................................................................... 341
10.1
10.2
Content
COMMENTS ...............................................................................................................................341
IDENTIFIERS ..............................................................................................................................341
7
10.2.1
Reserved Words ..............................................................................................................341
10.2.2
Qualification ...................................................................................................................342
10.2.3
Naming Conventions .......................................................................................................342
10.3
JAG ELEMENT............................................................................................................................343
10.3.1
Jag Element Properties ...................................................................................................343
10.4
LOAD ELEMENT ........................................................................................................................344
10.4.1
Load Element Properties ................................................................................................345
10.4.2
Data Types ......................................................................................................................345
10.4.2.1
10.4.2.2
10.4.2.3
10.4.2.4
10.4.2.5
10.4.2.6
10.4.2.7
10.4.2.8
Atomic Types ........................................................................................................................... 346
Composite Types...................................................................................................................... 346
User-defined Types .................................................................................................................. 347
Native Type .............................................................................................................................. 347
Vague Type .............................................................................................................................. 347
Void Type ................................................................................................................................ 347
Implicit Type Cast .................................................................................................................... 347
Explicit Type Cast .................................................................................................................... 348
10.5
LITERALS ..................................................................................................................................348
10.5.1
Atomic Literals ................................................................................................................348
10.5.1.1
10.5.1.2
10.5.1.3
10.5.1.4
10.5.1.5
10.5.1.6
10.5.1.7
10.5.1.8
10.5.1.9
10.5.2
10.5.2.1
10.5.2.2
10.5.2.3
10.5.2.4
Boolean Literals ....................................................................................................................... 348
Character Literals ..................................................................................................................... 349
Integer Literals ......................................................................................................................... 349
Real Literals ............................................................................................................................. 349
String Literals........................................................................................................................... 350
Symbol Literals ........................................................................................................................ 350
Date Literals ............................................................................................................................. 350
Binary Literals ......................................................................................................................... 351
XML Literals ........................................................................................................................... 351
Composite Literals ..........................................................................................................351
Vector Literals ......................................................................................................................... 351
List Literals .............................................................................................................................. 352
Map Literals ............................................................................................................................. 352
Object Literals .......................................................................................................................... 353
10.6
EXPRESSIONS ............................................................................................................................354
10.6.1
Unary Operators .............................................................................................................354
10.6.1.1
10.6.1.2
10.6.1.3
10.6.1.4
10.6.1.5
10.6.1.6
10.6.1.7
10.6.1.8
10.6.1.9
10.6.2
10.6.2.1
10.6.2.2
10.6.2.3
10.6.2.4
10.6.2.5
10.6.2.6
10.6.2.7
10.6.2.8
10.6.2.9
10.6.2.10
10.6.2.11
10.6.2.12
10.6.2.13
10.6.2.14
10.6.2.15
10.6.3
8
+ Operator ................................................................................................................................ 354
– Operator ................................................................................................................................ 354
++ Operator .............................................................................................................................. 354
-- Operator................................................................................................................................ 354
! Operator ................................................................................................................................. 355
~ Operator ................................................................................................................................ 355
Typeof() Operator .................................................................................................................... 355
Valueof() Operator ................................................................................................................... 355
Arg() Operator ......................................................................................................................... 356
Binary Operators ............................................................................................................356
= and @= Operators ................................................................................................................. 356
+ and += Operators .................................................................................................................. 357
– and –= Operators ................................................................................................................... 358
* and *= Operators ................................................................................................................... 359
and /= Operators ....................................................................................................................... 359
% and %= Operators ................................................................................................................ 359
Equality (==, ?=, !=, ===, !==) Operators ............................................................................... 359
Relational (<, <=, >, >=) Operators.......................................................................................... 360
Logical (&&, ||) Operators ....................................................................................................... 360
Bitwise (&, &=, |, |=, ^, ^=) Operators ..................................................................................... 360
Bitwise Shift (<<, <<=, >>, >>=) Operators ............................................................................ 361
List/vector/map access Operator .............................................................................................. 361
Object access (. and ?.) Operators ............................................................................................ 361
@ Operator............................................................................................................................... 362
Instanceof Operator .................................................................................................................. 363
Ternary Operators ..........................................................................................................363
JavaGram Agile Development
10.6.3.1
10.6.3.2
10.6.4
10.6.4.1
10.6.4.2
10.6.4.3
10.6.4.4
10.6.4.5
Conditional Expressions........................................................................................................... 363
Asynchronous Method Invocation ........................................................................................... 363
N-ary Operators ..............................................................................................................365
Vector Operator........................................................................................................................ 365
List Operator ............................................................................................................................ 365
Map Operator ........................................................................................................................... 365
New Operator ........................................................................................................................... 366
Object Operator ........................................................................................................................ 366
10.6.5
Operator Precedence ......................................................................................................367
10.6.6
Delayed Strings ...............................................................................................................367
10.6.7
Method Invocation ..........................................................................................................368
10.7
STATEMENTS .............................................................................................................................368
10.7.1
Expression Statement ......................................................................................................368
10.7.2
Blocks ..............................................................................................................................368
10.7.3
If Statement .....................................................................................................................369
10.7.4
While Loop ......................................................................................................................369
10.7.5
Do Loop ..........................................................................................................................370
10.7.6
For Loop .........................................................................................................................370
10.7.7
For-in Loop .....................................................................................................................371
10.7.8
Break Statement ..............................................................................................................371
10.7.9
Continue Statement .........................................................................................................371
10.7.10
Return Statement .............................................................................................................372
10.7.11
Switch Statement .............................................................................................................372
10.7.12
Throw Statement .............................................................................................................373
10.7.13
Try Statement ..................................................................................................................373
10.7.14
Synchronized Statement ..................................................................................................374
10.7.15
Query Statement ..............................................................................................................374
10.7.16
Transaction Statement ....................................................................................................375
10.7.17
Assert Statement ..............................................................................................................375
10.8
CLASSES ....................................................................................................................................375
10.8.1
Class qualifiers ...............................................................................................................376
10.8.2
Class Properties ..............................................................................................................377
10.8.3
Fields ..............................................................................................................................377
10.8.3.1
10.8.3.2
10.8.4
10.8.4.1
10.8.4.2
10.8.4.3
10.8.4.4
10.8.4.5
10.8.4.6
10.8.4.7
10.8.4.8
10.8.4.9
10.8.4.10
10.8.4.11
10.8.5
10.8.5.1
10.8.5.2
10.8.5.3
10.8.5.4
10.8.5.5
10.8.6
10.8.6.1
10.8.6.2
10.8.7
10.8.8
Content
Field Qualifiers ........................................................................................................................ 377
Field Initialization .................................................................................................................... 378
Methods...........................................................................................................................378
Method Qualifiers .................................................................................................................... 379
Constructors ............................................................................................................................. 380
Invoking Methods .................................................................................................................... 380
This .......................................................................................................................................... 381
Default Arguments ................................................................................................................... 381
Variable Number of Arguments ............................................................................................... 381
Method Overloading ................................................................................................................ 382
Type Checking ......................................................................................................................... 382
Remote Methods ...................................................................................................................... 382
Targeted Remote Calls ............................................................................................................. 384
Local Methods ......................................................................................................................... 384
Inheritance ......................................................................................................................385
Abstract Methods ..................................................................................................................... 385
Polymorphism .......................................................................................................................... 386
Super ........................................................................................................................................ 386
Mutual Classes ......................................................................................................................... 386
Subclass Constructor Rules ...................................................................................................... 387
Text Members ..................................................................................................................388
Text Properties ......................................................................................................................... 389
Text Qualification .................................................................................................................... 389
GUI Members .................................................................................................................390
Static Initialization Blocks ..............................................................................................390
9
10.8.9
Singleton Classes ............................................................................................................390
10.8.10
Remote Classes ...............................................................................................................391
10.9
EXCEPTIONS ..............................................................................................................................392
10.10 THE SYS PSEUDO CLASS ...........................................................................................................393
10.10.1
Sys Attributes ..................................................................................................................393
10.10.2
Sys Methods ....................................................................................................................395
10.10.2.1
10.10.2.2
10.10.2.3
10.10.2.4
10.10.2.5
10.10.2.6
10.10.2.7
10.10.2.8
10.10.2.9
10.10.2.10
11
Parsing and Evaluation............................................................................................................. 395
File Handling ........................................................................................................................... 396
Input/Output ............................................................................................................................. 400
Debugging ................................................................................................................................ 401
String Handling ........................................................................................................................ 402
Client-server ............................................................................................................................. 407
Maths ....................................................................................................................................... 413
Date .......................................................................................................................................... 415
Collections ............................................................................................................................... 416
Miscellaneous ..................................................................................................................... 419
GUI REFERENCE .......................................................................................................................... 424
11.1
MARKUP....................................................................................................................................424
11.1.1
Element Qualifiers ..........................................................................................................425
11.1.2
Element Identifiers ..........................................................................................................425
11.1.3
Element Properties..........................................................................................................426
11.1.3.1
11.1.3.2
Event Handlers ......................................................................................................................... 426
Data Models ............................................................................................................................. 427
11.1.4
Boiler Plates ...................................................................................................................428
11.2
ELEMENT TYPES........................................................................................................................428
11.2.1
Abstract Elements ...........................................................................................................428
11.2.2
Application Elements ......................................................................................................432
11.2.3
Window Elements ............................................................................................................433
11.2.4
Layout Elements ..............................................................................................................436
11.2.5
Container Elements .........................................................................................................438
11.2.6
Menu Item Elements ........................................................................................................442
11.2.7
Decorative Elements .......................................................................................................443
11.2.8
Field Elements ................................................................................................................444
11.2.9
Selection Elements ..........................................................................................................446
11.2.10
Button Elements ..............................................................................................................449
11.2.11
Feedback Elements .........................................................................................................449
11.2.12
Area Elements .................................................................................................................450
11.2.13
Tree Elements .................................................................................................................450
11.2.14
Table Elements ................................................................................................................452
11.2.15
Grid Elements .................................................................................................................455
11.2.16
Graph Elements ..............................................................................................................459
11.2.17
Gadget Elements .............................................................................................................468
11.2.18
Map Elements .................................................................................................................469
11.2.19
Jade Elements .................................................................................................................472
11.2.20
Reference Elements .........................................................................................................473
11.2.21
Invisible Elements ...........................................................................................................474
11.3
THE GUI PSEUDO CLASS ...........................................................................................................476
11.3.1
Gui Attributes ..................................................................................................................476
11.3.2
Gui Methods ....................................................................................................................476
12
SQL REFERENCE .......................................................................................................................... 482
12.1
QUERY AND TRANSACTION STATEMENTS .................................................................................482
12.2
MARKUP....................................................................................................................................482
12.2.1
Field Translation ............................................................................................................482
12.2.2
Text.sql ............................................................................................................................482
12.2.3
Text.sql.query ..................................................................................................................483
10
JavaGram Agile Development
12.2.4
Text.sql.update ................................................................................................................484
12.2.5
Text.sql.prepare ..............................................................................................................484
12.2.6
Text.sql.callable ..............................................................................................................484
12.2.7
Element Properties..........................................................................................................485
12.2.8
Capability Summary........................................................................................................486
12.3
DATABASE CONNECTION CONFIGURATION ...............................................................................487
12.4
THE SQL PSEUDO CLASS ...........................................................................................................488
12.5
BUSINESS OBJECT MANAGER ....................................................................................................495
12.6
THE BOM PSEUDO CLASS ..........................................................................................................497
13
LIBRARY REFERENCE................................................................................................................ 500
13.1
13.2
13.3
13.4
13.5
13.6
14
LIB/LANG/ .................................................................................................................................500
LIB/SVR/ ....................................................................................................................................502
LIB/IO/ .......................................................................................................................................503
LIB/GUI/ ....................................................................................................................................504
LIB/BOM/ ...................................................................................................................................511
LIB/SQL/ ....................................................................................................................................513
JADE – JAVAGRAM IDE .............................................................................................................. 514
14.1
OVERVIEW ................................................................................................................................514
14.2
PROJECTS ..................................................................................................................................515
14.2.1
Project Operations ..........................................................................................................515
14.2.2
Project Tree ....................................................................................................................516
14.2.3
Outline Tree ....................................................................................................................518
14.2.4
Project Properties ...........................................................................................................519
14.2.5
Project Statistics .............................................................................................................523
14.2.6
Building a Project ...........................................................................................................523
14.3
EDITOR ......................................................................................................................................524
14.3.1
File Operations ...............................................................................................................525
14.3.2
Editing Operations ..........................................................................................................527
14.3.3
Code Insight ....................................................................................................................529
14.3.4
Reformatting a File .........................................................................................................530
14.3.5
Viewing a Script in HTML ..............................................................................................531
14.4
SEARCHING AND REFACTORING ................................................................................................531
14.4.1
Searching a File ..............................................................................................................531
14.4.2
Searching a Project.........................................................................................................532
14.4.3
Refactoring......................................................................................................................533
14.5
RUNNING ...................................................................................................................................533
14.6
DEBUGGING ..............................................................................................................................534
14.7
TOOLS .......................................................................................................................................537
14.7.1
Import Java Classes ........................................................................................................537
14.7.2
Preferences .....................................................................................................................539
15
JAVAGRAM SYNTAX ................................................................................................................... 543
15.1
15.2
15.3
Content
CONVENTIONS ...........................................................................................................................543
JAG PRODUCTION RULES .........................................................................................................543
JTP PRODUCTION RULES...........................................................................................................553
11
1 Introduction
JavaGram is a new technology specifically designed to support agile development. As a
language, it shares many of Java’s features – syntax, platform independence, strong type
checking, object orientation, and garbage collection – but also offers capabilities that
make it a much easier-to-use and productive platform, such as declarative programming,
automatic remoting, asynchronous method invocation, and dynamic loading.
JavaGram enables you to deploy a standalone or multi-tier client-server application from
a single code base, allowing you to run your client in an Internet browser as well as a
native desktop application. It manages the underlying complexities of distributed
applications for you so that you can focus on what matters most: implementing business
functionality. Furthermore, time consuming activities such as application maintenance
and patching are significantly simplified through a server-centric release process that
automatically updates all dependent deployment points.
The technology is simple and easy to learn; yet the benefits are overwhelming – up to an
order of magnitude gain in productivity and robustness when compared to conventional
technologies.
1.1 Background
There has been growing interest in recent years in agile software development methods.
The shortcomings of traditional, waterfall methods have been known for at least three
decades, namely:

The requirements for a complex system can rarely be specified fully and accurately in
advance.

Despite tight quality control, the output of each phase will contain gaps or flaws that
won’t be discovered until a later phase.

User requirements will evolve during the course of a long project, thus making the
end-result inconsistent with the latest requirements.

The cost of fixing a requirement or design defect discovered later in the project is
substantial.

Given that a working system is not available until late in the project, there is little
opportunity for user participation and feedback; this increases the risk of the system
not being accepted by the users.

These challenges often cause schedule delays and budget blowouts.
The agile approach attempts to address these difficulties by promoting a more iterative
lifecycle, where emphasis is on prototyping, user participation, having a working system
12
JavaGram Agile Development
all along, and less documentation. The approach is best summarized by the agile
manifesto (www.agilemanifesto.org), which places more value on:

individuals and interactions over processes and tools,

working software over comprehensive documentation,

customer collaboration over contract negotiation, and

responding to change over following a plan.
Successful adoption of the agile approach requires the overcoming of cultural, process,
and practice barriers. Although these challenges are primarily non-technological, the use
of suitable technology can be of substantial benefit. The reality is that most technologies
available today are not particularly well-suited to the agile approach, typically because
they either predate the ‘agile age’ or ignore its dynamics.
JavaGram has been specifically designed to support the dynamics of agile development.
Its conception is the result of over ten years of experience gained from successfully
practicing RAD and agile in commercial software projects. As such, it’s a technology
designed by a practitioner for practitioners.
1.1.1 Agility Criteria
So what makes a technology more suited to agile development than others? The answer
to this question lies in the dynamics of practicing agile in real-life projects.
Agile relies heavily on iterative development. The first priority in a project is to produce
a working prototype of a proposed system and then to use this as a vehicle for eliciting
detailed user requirements. As requirements emerge, these are used to further refine and
enhance the prototype. Enhancements are done as a series of mini development cycles
where during each cycle we design, code, and test the next increment, and invite users to
evaluate the outcome.
The cyclic and iterative nature of agile places great emphasis on going from requirements
to working software rapidly. Speed is of the essence; otherwise cycles become slow and
ineffective. Rapid delivery of the next cycle ensures that users will remain engaged and
the project will not lose momentum. At the same time, it’s important to keep the project
team small to avoid communication overheads and to minimize the need for detailed
documentation, both of which will slow things down.
In order to speed up each cycle, we must not only have the means to design and code
business functionality quickly, but also to rapidly turnaround defects raised during the
testing phase. When this is not the case, testing can become hopelessly inefficient, as
testers will spend most of their time waiting for critical fixes without which further
testing can’t take place.
Equally important is the cross cycle speed. If each subsequent cycle takes longer than the
previous one due to developers finding it difficult to add new functionality then
Introduction
13
momentum will be lost and eventually grind to a halt. It is therefore vital that the
application under development lends itself to alteration and evolution. While this is
primarily influenced by the quality and foresight of the original design, the underlying
technology can go a long way in promoting good practices that avoid design inflexibility.
Finally, agile is all about simplicity. Simplicity is achieved by untangling complexity so
that the essential is separated from the accidental. Once we’ve identified what’s essential,
the least complicated way of getting there has the potential to deliver the best outcome.
Without constant awareness of this, IT professionals have a tendency to be dazzled by
technological complexity and run the risk of over-engineering their solutions.
1.1.2 Barriers to Rapid Development
In order to design a language that facilitates speedy development, we must consider the
things that slow down developers; namely:

Language complexity. The more complex a language is, the steeper its learning
curve will be. Contrary to the popular opinion that a language’s complexity is a
function of its number of features and constructs, it’s primarily a product of giving the
programmer too many different ways of doing the same thing. Too much choice
leaves the programmer in a situation where they have to constantly think about
choosing the best approach, and this will slow down development. JavaGram avoids
this pitfall by not attempting to be a totally versatile and general purpose
programming language. For example, JavaGram offers only three container types –
lists, vectors, and maps – each of which has only one implementation. So when the
programmer sees the need for a container, no time is lost on deciding which one to
use.

Plumbing overhead. More than 50% of code in a typical application tends to be of
‘plumbing’ nature. This is code that doesn’t implement business functionality but
performs essential housekeeping. For example, data entered by the user into a screen
typically needs to be extracted, validated, stored in a suitable data structure defined by
the programmer, transmitted from the client to the server using a message defined by
the programmer, unwrapped on the server-side, processed by interacting with a
database, and so on. Typically, such data goes through a number of transformations
where it’s changed from one format to another, and yet another, until it finally can be
acted upon. JavaGram greatly reduces the need for plumbing code by performing
such tasks behind the scene without the programmer having to worry about them.
This saves the programmer much valuable time, enabling them to focus on actual
business functionality. Interestingly, because a lot of unnecessary transformation is
avoided, the end-to-end process executes faster, thus also saving computational
resources.

Procedural clutter. Most languages (including Java) require the programmer to think
procedurally. While this works well in some situations (e.g., implementing
transactions), it hinders tasks that are better suited to a declarative style. GUI
programming is one such task. Because GUIs are visual and typically hierarchical, a
declarative notation can be far more expressive and convenient for implementing
14
JavaGram Agile Development
them. JavaGram adopts this style by allowing the programmer to define GUIs using a
markup notation. This not only results in a lot less code, it also greatly enhances the
readability of the code, to the extent that the GUI can be easily visualized by simply
observing the code.

Static composition. Most languages take a static view of the components that
comprise a program and require all the referred components to be in place before the
program can be executed. Because of the incremental nature of agile, this is a major
inconvenience which forces the programmer to define placeholders and stubs to work
around the issue. JavaGram overcomes this problem by allowing the programmer to
dynamically load scripts. For example, if the action of a push button is defined by a
dynamically-loaded script, the programmer will be able to run the program even if
this script is not defined or is incomplete and has compilation errors. These errors will
not surface unless the user actually pushes the button.

Complex object model. In object-oriented programming, business objects can be a
source of substantial complexity. Most of this complexity is in the underlying
implementation of the business objects (e.g., persistence). However, from an agile
development viewpoint, it’s not the implementation that’s important but the business
functionality offered by the object. JavaGram reduces this complexity by offering a
straightforward implementation model based on a generic object which hides much of
the underlying complexity. By sub-classing the generic business object class and
using declarative schemas, the programmer can do away with time-consuming and
error-prone activities such as implementing object persistence.
1.1.3 Barriers to Rapid Testing
The key to an effective and fast testing cycle is the ability to turnaround defects quickly.
The usual pattern in system testing is that a tester runs a number of test cases and records
observed defects. Some of these defects become showstoppers, preventing the tester from
progressing any further. At this point the tester will need to wait until enough defects
have been fixed and a new release produced so that testing can continue.
The main barrier to quickly delivering a test release is a slow build process. JavaGram
addresses this by eliminating the need to produce an actual build. A release can simply
consist of the correct versions of the scripts that comprise the application, extracted from
a version control system and placed on a test application server. Compilation is not
required, as the application server will incrementally compile the scripts on demand. This
means that even a large application can be released in minutes.
Even better, for the majority of fixes, a complete release is not required. Developers can
choose to release only the few affected scripts that fix the outstanding defects. JavaGram
even allows an application to be hot fixed without restarting the server. Experience has
shown that critical defects can be turned around shortly after they’re raised by testers,
thus enabling testers to continue their work with minimal disruption. Similar benefits are
gained in production support.
Introduction
15
1.1.4 Barriers to Rapid Evolution
A live application is best regarded as an evolving entity. The more the application is used,
the more users will demand from it – active use leads to rapid evolution. When a new
application is designed, it’s almost impossible to foresee all the future demands that will
be placed on it. At any point in time, it’s only practical to consider features that are likely
to be demanded in the near to medium term. Future (and especially unforeseen) demand
is likely to stretch the application design to an extent that couldn’t have been predicted.
Long term therefore, the malleability of the design becomes a critical issue. A design
that’s not accommodative of change will eventually break and disrupt the evolution
process. So how can a design be made malleable? There are two competing views on this
subject.
The first view argues that considerable flexibility should be built into the design from the
beginning to make it future proof. This is intellectually appealing but in practice rarely
successful. The problem with this approach is that devising extensive design flexibility
can be very costly and invariably leads to design complexity, both of which go against
the agile principles. Experience indicates that, in the long run, very few of such expensive
flexibilities actually get used and the rest become a liability.
The second view argues for simplicity. By keeping a design as simple as possible, we not
only shorten the construction phase, we also make it easier for future developers to
understand its make-up. The latter is far more valuable than some appreciate. Most
practitioners would testify that the biggest hurdle in tweaking a design is to first
understand it. Once this is achieved, only a bit of developer creativity is required to work
out a way of accommodating something new.
JavaGram adheres to the simplicity view. The language helps the developer to express
things succinctly and with minimum clutter. Reduced plumbing code means that the vast
majority of code actually represents business functionality. This facilitates understanding
and makes it easier to work out how to best apply a change.
1.2 Salient Features
JavaGram builds on the strengths of Java and closely follows the Java syntax and
semantics, including strong type checking. This should make it very easy for a Java
programmer to become proficient in JavaGram.
This section summarizes those features of JavaGram that characterize it as an agile
programming language. While reading this summary, don’t worry if something is not
entirely clear to you or sounds too technical. The intent here is to give a flavor of
JavaGram in a limited space. Subsequent chapters will explore these topics in greater
depth and at a gentler pace.
16
JavaGram Agile Development
1.2.1 Server Centricity
Historically, programming languages have been designed with the assumption that
application code will need to be installed in its target environment before it can be
executed. The World Wide Web has been the most significant departure from this
paradigm. Under this model, a web application is not pre-installed on the client side, but
is incrementally downloaded in response to actions taken by the user. However, this
dynamically-downloaded code (HTML, which may also include JavaScript and the like)
is primarily concerned with presentation, and is generated by the actual application code
residing on the web server, hence the term thin client.
Web page is
delivered to client
Web
Browser
Client
World-wide-web
Web Server
Client requests
web page
http://www.acme.com/products
The design of JavaGram was inspired by this model, with a key difference: rather than
the server generating presentation code for the client, the server delivers executable code
to the client. Like the web model, the client initially contains no code at all, but rather is a
shell capable of receiving code and ‘interpreting’ it. In the web model, the client is a web
browser; in the JavaGram model, the client is a compact runtime environment called
JAG.
Introduction
17
Script is
delivered to client
JAG loads and
executes script
JAG
Client
World-wide-web
App Server
Client attempts to
Access a script
www.acme.com:443/search.jag
A JavaGram client boots itself against a JavaGram server using an initial address (similar
to a URL in a web browser), which identifies the server and the initial script. In response
to this, the server creates a session thread to handle all subsequent communication with
the client. The requested script is then sent to the client, which the latter loads and
executes. During the course of execution, the script may refer to other scripts, which are
sourced from the server in a similar manner. Therefore, the client code base is built
incrementally according to user actions.
Like the thin client model, the server-centric model of JavaGram has a number of
advantages over the traditional fat client model; namely:

There is no need to install an application at the client end.

An application can start quickly because the loading process is incremental.

Release management is much easier, as a new version of the application doesn’t need
to be installed on every client, just on the server(s). The latter is much easier because
servers are centralized and there is a lot less of them than clients – which are not only
numerous but also often out of reach.
The JavaGram model also retains some of the advantages of the traditional fat client
model; namely:

Unlike the thin client model which is stateless, the JavaGram model is stateful.
Because each client is allocated a dedicated session that lasts for the duration of the
connection, the session accurately reflects the server-side state of the client. This
eliminates the burden of additional programming normally required for thin clients to
keep track of state information.

JavaGram supports both synchronous and asynchronous requests, as opposed to the
asynchronous-only thin client model.

The programmer can delegate some of the application’s processing to the client end
(e.g., report generation, complex calculations) within the same code base. This
18
JavaGram Agile Development
provides more scope for load balancing and making sure that server(s) don’t become
a bottleneck.
For lack of a better term and to distinguish the JavaGram model from the fat client and
thin client models, we’ll subsequently refer to it as the hybrid client model.
1.2.2 Browser-based and Native Desktop Clients
A unique feature of JavaGram is that the same application code base can support
browser-based as well as native desktop clients. JavaGram achieves this by offering two
versions of its runtime environment:

The Flash version of the JavaGram runtime (JAG.swf) is written in ActionScript 3
(AS3) and runs within the Flash Player engine. When a browser-based client uses this
runtime, it’s automatically downloaded from the web server.

The Java version of the JavaGram runtime (JAG.jar) is utilized by native desktop
clients. The same runtime is also used by JavaGram servers as well as standalone
applications.
Both runtimes are very compact and therefore carry little download overheads.
1.2.3 Static and Dynamic Loading
JavaGram support static as well as dynamic loading of scripts. Static loading is suitable
for specifying cross-script dependencies. Dynamic loading is suitable for situations where
we don’t want a script to be loaded until it’s activated by a trigger (e.g., the user
performing an action). In both cases, the requested script may be sourced locally or from
an application server.
The JavaGram application server uses an implicit form of dynamic loading to serve
remote calls received from a client. When a remote call is received by a server, the
underlying message includes the path of the script that contains the definition of the
target class. The server uses this path to dynamically load the script if it’s not already
loaded. This ensures that the loading of scripts on the server-side is automatic, demanddriven, and hence not a burden on the programmer.
1.2.4 Code Caching
To minimize client-server traffic, JavaGram employs a comprehensive caching
mechanism that maintains an active cache on both the client and server ends.
On the client side, the cache is somewhat similar to the local cache of a web browser, but
is more deterministic. Whereas a web browser refreshes the pages in its cache based on
their age, JavaGram requires an exact timestamp match. This is necessary in order to
ensure the consistency of the versions of scripts that make up an application.
Introduction
19
The server-side cache serves a different purpose. Because a JavaGram script can contain
code intended for clients and/or servers, each script is compiled to produce two variants –
one for the client-side (from which server-related information is removed) and one for the
server-side (from which client-related information is removed). These binary files are
deposited into the server-side cache. This cache is automatically updated when a script is
modified.
1.2.5 Built-in Types
JavaGram provides the following built-in types.

Atomic Types:

boolean (similar to Java boolean)

char (similar to Java char)

int (similar to Java long)

real (similar to Java double)

string (similar to java.lang.String).

symbol (like string except that multiple instances having the same representation
are stored only once)

date (date and time)

stream (mechanism for performing IO with respect to a file, buffer, or client-
server communication channel)


Composite Types:

vector (contiguous sequence of values, with random access)

list (sequence of values, without random access)

map (key-value pairs, with random access)

object (opaque instances of user-defined types, similar to java.lang.Object)
Pseudo Types:

vague (can represent any type)

native (Java values with no equivalent type in JavaGram)

void (absence of a value)
Values for atomic and composite types can be created literally (except for stream) or
programmatically.
1.2.6 Object Orientation
The OO features of JavaGram are very similar to Java, with the following notable
exceptions.
20
JavaGram Agile Development

Support for multiple inheritance.

Support for remote methods and classes, both of which are managed transparently to
the programmer.

Support for GUI members (behave like class fields and allow you to define user
interface components declaratively and hierarchically).

Support for text members (behave like methods and allow you to do advanced text
processing).

Support for SQL members (behave like methods and allow you to isolate your SQL
code).

Support for singleton classes.

Support for object literals (class instances that are created at load time rather than
runtime).

Ability to limit the visibility of a class method to client or server side.

Ability to specify default argument values for methods.
1.2.7 Multiple Inheritance
Multiple inheritance (MI) is a powerful design tool that, when used judiciously, can
simplify a design and reduce development effort. Unfortunately, MI has attracted plenty
of bad publicity due to complex realizations (e.g., C++) that programmers have struggled
with. JavaGram attempts to remedy this using mutual classes:

A derived class can have multiple base classes, provided at most one of them is nonmutual.

All the base classes of a mutual class (if any) must also be mutual.

Mutual classes that are inherited more than once in a class hierarchy (as in the
‘dreaded diamond’ problem) are treated as if they are inherited once. Therefore, an
instance of the derived class contains only one instance of the mutual class’s fields. In
this sense, mutual classes behave like virtual base classes in C++.
1.2.8 Automatic Remoting
Remote methods represent one of the most powerful features of JavaGram. They make
the task of writing client-server applications exceptionally easy by removing the burden
of having to deal with data communication, synchronization, hand-shaking, error
handling, and so on. As a result, invoking a remote method on a server becomes as easy
as invoking a local method. Hiding all this complexity has the added benefit of allowing
the programmer to easily use the same code in different deployment models (standalone
versus distributed).
JavaGram also allows you to define an entire class as remote so that all its actual
processing is handled on the server-side. When a client obtains a reference to a remote
Introduction
21
object, it receives a proxy object instead. Any operation performed on the proxy is
transparently applied to the remote object.
In practice, a client has no way of knowing whether it’s dealing with a remote
method/class or a local one. This ensures that code designed to be deployed as clientserver will also work when run as standalone. The obvious benefit of this is a simplified
testing process – developers can develop and test standalone and later switch to clientserver when the code is more fully developed.
JavaGram’s exception handling works seamlessly across the client-server boundary. For
example, an exception raised (on the server-side) by a remote method is delivered to the
caller (on the client-side) as if it were a locally raised exception.
1.2.9 Asynchronous Method Invocation
Asynchronous method invocation allows you to call a (local or remote) method such that
you don’t have to wait for it to complete. Execution proceeds to the next statement as
soon as the call is made. When the call eventually completes, a callback is invoked to
complete the processing. If an error callback is also specified and the method throws an
exception then the error callback is invoked instead.
1.2.10 Declarative GUIs
JavaGram offers a completely different style of GUI programming to Java’s Swing.
Whereas GUI programming in Swing is procedural, JavaGram allows you to define a
GUI declaratively. This has a number of advantages: you write a lot less code, the code is
much more readable, and the code readily portrays the hierarchical structure of the GUI.
As a result, creating sophisticated GUIs in JavaGram is much easier than in Java.
GUI members are defined using a markup notation. This markup notation can be
extended by the programmer to devise new and novel components.
1.2.11 Parameterized Text
Programs often have to do some level of text manipulation. In most programming
languages, this is done through string concatenation (e.g., using the + operator or the
StringBuilder class of Java). The end result is rather messy and difficult to visualize due
to the procedural nature of the code.
JavaGram offers two facilities to simplify text handling. The first is called delayed strings
and is useful for simple text parameterization tasks. For more elaborate tasks, JavaGram
provides text class members. A text member is like a method and can be used to handle
parameterized text.
Like GUI members, text members are defined using a markup notation which is
extensible. In fact, JavaGram provides a number of such extensions for SQL handling.
22
JavaGram Agile Development
1.2.12 Database Interaction
In JavaGram, interaction with databases is facilitated by the sql pseudo class.
Additionally, a number of parameterized text constructs are provided to make SQL
formation straightforward and consistent with the declarative style of JavaGram.
One of the readability benefits of the JavaGram style of SQL programming is that all
SQL commands are localised to <text.sql...> markups and are hence easily identifiable.
This is superior to the traditional style where SQL is freely sprinkled throughout the code.
1.2.13 Serialization and Parsing
A useful feature of JavaGram is that any value (other than stream and native values, but
including class instances) can be serialized to clear text, as well as parsed without any
extra programming effort. The resulting benefits are:

Complex data structures (such as meta data) can be pre-created in code. This is
convenient as well as self-documenting.

Programs can be debugged more easily.

Composite data can be stored in a single database column and retrieved easily, thus
facilitating much simpler data models.

The data exchanged between client and server ends can be easily viewed in a readable
textual format.
The ability to parse data or code ‘on the fly’ can be valuable in some programming
situations. It’s especially useful in agile programming, as it can substantially reduce
coding and maintenance effort.
1.2.14 Business Objects
JavaGram provides a pseudo class (bom) and a library class (Object) that together provide
a convenient framework for working with business objects that require persistence. By
sub-classing Object, you can quickly develop a persistent business object without writing
any SQL. The relationship between the object’s structure and the underlying database
table is specified by the programmer as meta data, which enables Object to work out how
to map the data.
1.2.15 Java Interoperation
JavaGram provides a simple facility for interoperating with its underlying
implementation language (Java). The sys.java() method uses reflection to allow any
Java class or method be accessed, and automatically maps data between JavaGram types
and Java types. This method is rarely used, but is a useful last resort when the
programmer needs to do something that JavaGram doesn’t directly support.
Introduction
23
1.3 Implementation
JavaGram has been developed in pure Java (and AS3) and is therefore platform
independent. The implementation is comprised of four parts:

JavaGram runtime environment (JAG) in two versions (Java and AS3).

JavaGram Development Environment (JADE)

JavaGram Server Monitor (JSM)

JavaGram standard library scripts
The AS3 (Flash) version of JAG comes in three flavor for use in a web browser, Adobe
AIR, or Android.
The overall architecture of JavaGram is illustrated by the following diagram, and the
components described below.
1.3.1 JavaGram Runtime Environment
JAG is packaged as a single JAR file (JAG.jar). It’s very compact (currently around 1.78
MB) and provides a complete environment for deploying and running JavaGram
applications, including:

Parser, analyzer, and evaluator for interpreting JavaGram code

Compiler for converting JavaGram code into binary format

JavaGram Transfer Protocol (JTP) for client-server messaging (over socket, HTTP, or
HTTPS)
24
JavaGram Agile Development

Application server (for deploying JavaGram servers)

Proxy server (for failover and load balancing application servers)
Every JavaGram client or server is deployed using JAG. A server deployment also
requires a configuration file which specifies the server settings (for security, data
compression, database connection pools, session management, etc.), as well as any other
JAR files used (e.g., JDBC drivers, FOP library, etc.)
All JavaGram servers are generic and exceptionally easy to setup and deploy (taking only
a few minutes). The same application server instance may serve many different
applications. JavaGram code is deployed into an application server by simply dropping it
into a nominated directory.
One of JavaGram’s important promises is that a JAG client (Java version) needs to be
installed only once (Flash version is automatically downloaded by the browser). The
process is like this. The user downloads and runs a small installation program (which
installs JAG.jar, a small Java keystore, and a shortcut to an application’s URL) on the
user’s machine. When the user runs the shortcut, the application is automatically booted
(scripts are demand-downloaded as necessary and executed). Application maintenance is
totally transparent to the user:

When a new version of the application is released to a server (i.e., revised scripts),
these revisions automatically find their way to the client installations.

If a new version of JAG.jar is released to a server, this can be automatically
propagated to all clients that depend on the changes in this JAR, causing the new JAR
to be automatically downloaded by the affected clients, and replacing the old JAR.
Furthermore, application code changes that leave class schemas unchanged (i.e., only
affecting the implementation of methods) can be deployed without restarting a server or
any client. This allows emergency hot fixes to servers without disruption to users.
JavaGram application servers are highly scalable. Multiple servers can be deployed in
either a failover or load balanced configuration. Where load balancing is used, traffic is
routed via one or more proxy servers, which in turn match clients against server instances
based on their load.
1.3.2 Compilation
The JavaGram compilation process is straightforward. A script is parsed, analyzed, and
converted to an equivalent byte code. When a server internally compiles a script, it
produces two compiled versions, one for client-end and one for server-end. Either version
excludes information that’s not relevant to its intended target environment. Scripts can
also be explicitly compiled, in which case a single version is produced that’s equivalent
to the source. Explicit compilation is suitable for cases where an application is to be
deployed in binary rather than source format (e.g., for intellectual property reasons).
Introduction
25
The JavaGram parser can parse scripts in source or binary format. The latter has the
advantage of being more secure and more efficient – the parser has to do a lot less work.
The binary codec used by the compiler is version controlled, so if a client tries to load a
script that’s been encoded using an outdated codec, this is detected, causing a refresh of
that script from the server.
1.3.3 JavaGram IDE
The JavaGram IDE (packaged as JADE.jar) provides a productive visual environment for
developing, debugging, and running JavaGram applications. Key components include:

Projector for setting up and managing the source code for a project

Syntax-directed editor, which automatically color-codes your code

Code Insight for visual navigation of code elements and auto completion

Runner for configuring and running applications as standalone, client, or server

Debugger for setting up breakpoints, inspecting the runtime stack, and viewing the
values of variables

Auto Analyzer which automatically parses and analyzes your project’s code as you
type, works out dependencies, and visually highlights errors
For a detailed description of JADE, please refer to Chapter 14.
1.3.4 JavaGram Server Monitor
The Server Monitor is a companion tool for monitoring deployed servers. This tool is
implemented entirely in JavaGram and its source code is included in the JavaGram
release. Its functionality includes:

Viewing the server sessions and managing them

Viewing server memory usage pattern

Viewing the server log, where diagnostic information is recorded

Uploading and downloading of files for investigation and patching purposes

Resetting the server and/or its database connection pools

Temporarily suspending user access for maintenance purposes
For a detailed description of JSM, please refer to Chapter 7.
1.3.5 JavaGram Standard Library Scripts
JavaGram comes with a standard library in source format. The scripts in this library
provide you with a set of reusable classes for a variety of tasks, including GUI
development, server deployment, SQL handling, business object management, data
26
JavaGram Agile Development
export, etc. Many of the examples presented in this book utilize these scripts and
illustrate their use. The obvious benefit of using the library is productivity.
The standard library scripts are detailed in Chapter 13.
1.4 Download and Installation
To setup a working JavaGram environment so that you can do development and try out
the examples presented in this book, you need to download and install the following
components.

Download the latest JRE or JDK from java.sun.com and install it, unless this is preinstalled on your computer.

If you intend to use a browser client, download and install the Flash Player from
www.adobe.com, unless this is pre-installed on your computer. Similarly, if you
intend to use an Adobe AIR client, also download and install Adobe AIR.

Download the latest JavaGram release from www.pragsoft.com and install it. This
installation is required on any computer where you intend to run a JavaGram program
(be it standalone, client, or server). When installing on a computer intended for
development work, you should also choose the option for installing JADE.

Download the latest transactional MySql release from www.mysql.com and install it.
All the SQL examples in this book have been developed with MySql, but you can use
any other database engine that supports JDBC. However, if you use another JDBCcompliant product (e.g., Oracle, SqlServer, Firebird), you may need to alter the syntax
of some of the SQL examples to conform to that product.
Introduction
27
2 Fundamentals
JavaGram programs consist of scripts. Each script is a text file whose name ends in .jag
(e.g., Test.jag). Compiled script names end in .jax (e.g., Test.jax). Each script defines
one or more classes and may refer to classes in other scripts. Such dependencies are
specified through the load facility, which enables one script to load others during its own
loading or, dynamically, during execution.
2.1 Example
The minimal syntax of a script is exemplified below.
HelloWorld.jag
<jag domain="doc/code/chap2">
class HelloWorld {
public static void main () {
sys.println("Hello World!");
}
}
</jag>
You can run it from a DOS command line like this:
java -cp JAG.jar jag.run.Env C:/JavaGram/doc/code/chap2/HelloWorld.jag
It will produce the following output.
Hello World!
Let’s look at the contents of this script and their meaning.
The code for a JavaGram script is always enclosed by a <jag></jag> pair, where <jag>
marks the beginning of the script and </jag> marks the end of it. As in HTML and XML,
a JavaGram markup can have properties. For example, the <jag> markup here has a
domain property. This is somewhat similar to the package construct in Java, in that it
defines a namespace for the script. As a result, the class defined in this script has a short
name (HelloWorld) and a long name (doc\code\chap2\HelloWorld). The latter is called a
qualified class name. Ordinarily, we use the short name of a class, but if two or more
classes defined in different domains have the same name, the qualified class name can be
used to avoid ambiguity.
You may have noticed that we’ve used forward slashes in the domain name and
backward slashes in the qualified name (Java uses periods for both). The forward slash
notation is actually a convenience. A domain can also be specified using backslashes. For
example:
28
JavaGram Agile Development
<jag domain="doc\\code\\chap2">
Because a backslash must be escaped inside a string, this notation is a little inconvenient,
so the forward slash convention is usually used instead. When using a qualified class
name in code however, only the backslash notation is allowed, as forward slashes are
treated as the division operator.
The rest of the script defines a simple class called HelloWorld. A class represents a userdefined type, and is a way of packaging data (fields) and behavior (methods) so that it
can be conveniently used elsewhere in the program. A JavaGram program is simply a
collection of classes that refer to one another.
It’s worth noting that, unlike Java, the name of a JavaGram class and its script file need
not match. You can also put multiple classes in the same script file. For each class,
JavaGram internally records the script that contains it and uses this information to locate
the right script when needed.
The definition of a JavaGram class consists of the keyword class, followed by the class
name, followed by the class body. The latter is enclosed by a pair of braces:
class HelloWorld {
...
}
A class name must be a valid identifier (a sequence of letters, digits, or underscores, but
not starting with a digit). The recommended convention in JavaGram is to capitalize the
first letter of every word of a class name.
The HelloWorld class contains just one method called main:
public static void main () {
sys.println("Hello World!");
}
Like a class name, a method name must be a valid identifier. The recommended
convention is to write method names in camel case (capitalize the first letter of every
word except for the first word).
A method is a recipe for computation – a sequence of instructions to do something. The
keywords public and static appearing at the beginning of the method are called
qualifiers. A qualifier imposes a certain rule on the thing that it qualifies. For example,
the public qualifier gives main public visibility, so that it can be accessed outside the
class; and the static qualifier makes the method accessible even without having class
instances (more on this later).
Fundamentals
29
Every method must have a return type (this appears before the method name) which
specifies the type of value the method will return when it’s called. The return type void is
a pseudo type, implying the absence of a value. In other words, main does not return
anything.
The empty pair of parentheses appearing after the method name means that main has no
parameters. The qualifiers, return type, name, and parameters of a method are collectively
called its signature, so main has the following signature:
public static void main ()
Finally, the body of the method appears last and (like a class body) is enclosed by a pair
of braces. Generally, the body of a method consists of statements, where each statement
specifies a specific instruction or computation. Being a very simple method, main has just
one statement:
sys.println("Hello World!");
The effect of this statement is to write the string "Hello World!" to standard output.
(Standard output is a predefined stream, normally tied to the screen in which you run
your program, causing output to be displayed in that screen.) This statement is itself a call
to a method (println) of another class (sys). The latter is a pseudo class built into JAG.
The term ‘pseudo class’ applies to predefined classes that behave like normal classes but
can’t be instantiated or extended.
In JavaGram, every statement must be terminated by a semicolon. A semicolon on itself
is also considered a statement (an empty statement).
As a rule, if a class has a main method that is public static void and with no parameters
(as HelloWorld does) then this method is treated specially – when such a class is loaded,
this method is automatically called. This is usually the starting point of execution for a
program.
2.2 Loading Scripts
Before it can be executed, a script must be loaded into JAG. There are three ways for
loading a script:

As a command line argument to JAG.jar. Use this method to run the initial script of a
JavaGram program.

Using the <load> markup inside a script. Use this method to document the
dependencies of a script on other scripts. This is called static loading – when the
enclosing script is loaded, it causes the enclosed scripts to be also loaded.

Using the sys.load() function. Use this method for dynamic loading of a script
during program execution. The script is loaded only when the sys.load() function is
executed. This is useful when you want to delay the loading of a script until it’s
30
JavaGram Agile Development
actually needed; for example, in response to the user pressing a button that initiates a
calculation, which is to be performed by the nominated script.
When you load a script using any of these methods, JavaGram processes the script in
three successive stages, as illustrated below.
During parsing, the script is checked for syntactic correctness. If syntactically correct, the
script is analyzed for semantic validity. Finally, if both these stages succeed, the script is
evaluated by evaluating its classes in the order in which they appear. The evaluation of a
class causes its static fields and static blocks to be evaluated. Also, if the class has a
public static void main method, it may be evaluated, depending on the load
configuration (this is always the case when loading from the command line).
Here is an example of how to load a script using the <load> markup.
CarTest.jag
<jag domain="doc/code/chap2">
<load>
"doc/code/chap2/Car"
</load>
class CarTest {
public static void main () {
Car car = new Car("Toyota", "Camry", 2007);
sys.println(car.format());
}
}
</jag>
It refers to another script which defines the class Car.
Car.jag
<jag domain="doc/code/chap2">
class Car {
protected string make;
protected string model;
protected int year;
public Car (string make, string model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
public string format () {
return make + " " + model + " " + year;
}
Fundamentals
31
}
</jag>
You can run this program using the command line:
java -cp JAG.jar jag.run.Env -root C:/JavaGram doc/code/chap2/CarTest.jag
It produces the following output.
Toyota Camry 2007
Note how we’ve used the –root option to specify a root directory for scripts. By default,
scripts appearing inside the <load> markup are relative to this directory. So
doc/code/chap2/Car.jag resolves to C:/JavaGram/doc/code/chap2/Car.jag.
You can specify multiple file paths within a <load> markup, each of which must be a
string. The strings must be separated by whitespace. The recommended convention is to
put each file path on a separate line.
Let’s discuss the contents of these two scripts and their meaning.
Car is a simple class that has three fields (make, model, year) and two methods (Car,
format). Fields are used to hold data. As with methods, fields may have qualifiers – all
three fields here are declared to be protected. This qualifiers means that these fields are
not visible outside the class; they’re only visible to the class members and members of
any classes derived from Car (derived classes are described later). Every field must have a
type which specifies the kind of data that the field can hold. Both make and model are
specified to be of type string (arbitrary sequence of characters enclosed in double
quotes) and year is specified to be of type int (integer number). A field name must be a
valid identifier. Like methods, the recommended convention is to write field names in
camel case.
The first method of the Car class has the same name as the class itself. This method is
called a constructor. You never specify a return type for a constructor because the return
type is implicit and is the class itself. Constructors are used to create instances of a class.
The distinction between a class and its instances is very important and fundamental to
understanding object-oriented programming. A class represents a potentially infinite
number of possibilities. For example, the Car class represents any car. However a specific
car (e.g., a 2009 Nissan Patrol) is represented by an instance of the Car class. Therefore, a
class is an abstraction (a concept) whereas its instances are concrete things (also called
objects) – hence the term ‘object-oriented programming’. When a class instance (object)
is created, a piece of memory is allocated to represent the object. The data for the class
fields is actually stored in this piece of memory.
Let’s look at the definition of the Car constructor:
32
JavaGram Agile Development
public Car (string make, string model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
This method takes three parameters. These appear after the method name, within a pair
of parentheses, and separated by commas. Each parameter consists of its type and an
identifier. The job of this constructor is to assign a value to each of the three class fields
using a corresponding parameter. The assignments are done using the = operator, which
copies the value represented by its right operand to the location represented by its left
operand (this is called an lvalue – something that can appear on the left-side of an
assignment). Because the parameters have the same name as the class fields, we’ve used
the this.id notation to refer to the fields. So, for example, the first assignment this.make
= make copies the value of the make parameter to the make field. this is a reserved word
that can only be used in non-static methods – it refers to the object on which the method
is invoked.
As an aside, the initial value of class fields (that haven’t been explicitly initialized at the
time of definition) is somewhat different to Java. In Java, an int field, for example, has
an initial implicit value of 0, and a String field has an initial value of null. In JavaGram,
every field, regardless of its type, has an initial value of null.
The second method (format) returns a string representation of a car.
public string format () {
return make + " " + model + " " + year;
}
The string is produced using the string concatenation operator +. This operator can take
operands of various types (e.g., strings, numbers) and joins together their string
equivalent. The return keyword causes the resulting string to be returned as the overall
value of the method.
The main method of the CarTest class illustrates how the Car class may be used.
public static void main () {
Car car = new Car("Toyota", "Camry", 2007);
sys.println(car.format());
}
The first statement in this method uses the new operator to instantiate the Car class to
create an object. This operator must always be followed by a call to a constructor method.
Because the constructor for Car has three parameters, a call to it must provide three
arguments. The types of these arguments must match the types of the corresponding
parameters. The newly created object is assigned to a local variable (car).
Fundamentals
33
The second statement invokes the format method on the object represented by car. The
resulting string is then passed to sys.println to write it to standard output.
2.3 Expressions and Statements
The body of a method always consists of zero or more statements. These statements
represent the computational steps of the method or, in other words, its implementation.
Statements are composed hierarchically – simpler statements may be combined to
produce more complex ones. When a statement is executed, it produces a side-effect,
such as altering the value of a variable, creating an object, or reading/writing data from/to
a stream.
Statements utilize another building block called expressions. Expressions also represent
computations but they’re different from statements in that they do not produce side
effects; they simply produce values. For example,
10 + 20
is an expression (it simply adds two numbers to produce a new value) whereas,
int n = 10 + 20;
is a statement because it stores the result of adding two numbers (an expression) in a
variable, thus altering the value of the variable (a side effect).
The reason this distinction is important is that the JavaGram syntax expects you to use
expressions in some places (e.g., the logical condition of an ‘if’ statement) and statements
in other places (e.g., the ‘then’ part of an ‘if’ statement), so you need to know which one
to use where. To make matters more confusing, some constructs can be used in either
capacity. For example, a call to a non-void method can be used either as an expression or
as a statement. So, in this case at least, the earlier observation that expressions don’t
produce side effects is not entirely true. It would be more accurate to say that most
expressions don’t cause any side effects.
As you read through the rest of this book, if you want to learn more about any topic, look
it up in the reference chapters. For example, to find out more about the methods of the
sys pseudo class, refer to their specification in Chapter 10. You might also find Chapter
15 useful – it describes the JavaGram syntax.
The rest of this chapter introduces the commonly used expressions and statements of
JavaGram using some simple examples that illustrate their purpose.
2.4 Types
The notion of type is fundamental to a proper understanding of programming.
Simplistically, a type represents a set of possible values. For example, the int type covers
34
JavaGram Agile Development
all integers. The relationship between a type (something abstract) and a value (something
concrete) is the same as the relationship between a class and its instances.
JavaGram is a strongly typed language, implying that:

Whenever you create a variable, you must specify its type. JavaGram will not allow
you to store a value in the variable that doesn’t belong to its type.

When you invoke a method, the type of each argument you pass to the method must
match the type of the corresponding parameter.
JavaGram comes with a number of predefined types (see Chapter 10 for details). These
represent types that you need in almost every program, so they’ve been built into the
language to ensure an efficient runtime implementation.
A JavaGram class is also considered to be a type and is called a user-defined type. In
fact, classes are the mechanism through which the JavaGram type system is extended. In
this sense, when you write a program, what you’re really doing is expanding the type
system. There is also a set of standard library classes (see Chapter 13) that provide
various useful types for use in your projects. These not only save you time but also serve
as ‘best practice’ because they encourage the use of proven design patterns.
If you’re new to object-oriented programming then the concept of type could take some
time to get used to, but this investment is vital if you’re to take full advantage of the
power of object-oriented programming. We’ll cover this topic in more detail in the next
chapter.
2.5 Control Flow
Any non-trivial program has to deal with possibilities – if a certain condition holds then
do this, otherwise do something else. This type of conditional behavior is realized by
statements that allow the program to take different execution paths based on logical
conditions. This concept is called control flow (or flow of control).
The simplest control flow statement is the if statement. Let’s look at a simple example
that illustrates its use. Consider the following class which represents bank accounts.
<jag domain="doc/code/chap2">
class BankAcc {
real balance;
static final real CREDIT_RATE = 0.045;
static final real DEBIT_RATE = 0.065;
public BankAcc (real balance) {
this.balance = balance;
}
public void applyInterest () {
if (balance >= 0)
balance += balance * CREDIT_RATE;
Fundamentals
35
else
balance += balance * DEBIT_RATE;
}
public static void main () {
BankAcc acc1 = new BankAcc(500);
BankAcc acc2 = new BankAcc(-200);
acc1.applyInterest();
acc2.applyInterest();
sys.println("Acc1 balance is ", acc1.balance);
sys.println("Acc2 balance is ", acc2.balance);
}
}
</jag>
The applyInterest() method uses an if statement to decide which interest rate to use,
based on the account balance. For an account in credit it uses CREDIT_RATE and for one in
debit, it uses DEBIT_RATE. The condition appearing after the if keyword must be a boolean
condition (something that evaluates to true or false). When it evaluates to true, the
statement after it is executed; otherwise, the statement after else is executed (the else
part is optional).
If you run this program, it will produce the following output.
Acc1 balance is 522.5
Acc2 balance is -213.0
Here are a few more observations about this program:

The balance field has no explicit visibility qualifier, therefore it’s deemed to have
domain visibility – it’s accessible by all classes defined in the same domain as this
class.

CREDIT_RATE and DEBIT_RATE are both defined to be static and final. This is how you
define constants in JavaGram. A final field must be initialized at the time of its
definition; its value cannot be subsequently altered. A static field is shared between
all instances of its parent class. So each time you create an instance of BankAcc,
memory is allocated for the balance field but not for CREDIT_RATE and DEBIT_RATE.

The += operator adds its left and right operands and stores the outcome in the left
operand (which must be an lvalue).

sys.println() accepts a variable number of arguments (zero or more), and sends the
string equivalent of each argument to standard output.

36
The text starting with // is called a comment. JavaGram ignores anything appearing
after // until the end of the line. Comments are useful for documenting additional
information about a program. There is another style of writing comments that can
span multiple lines: anything appearing between /* and */ is treated as a comment.
JavaGram Agile Development
There is also an expression form (called conditional expression) that can be used to
rewrite certain if-else statements more succinctly. For example, we can rewrite
applyInterest() in the following equivalent style.
public void applyInterest () {
balance += balance * (balance >= 0 ? CREDIT_RATE : DEBIT_RATE);
}
In a conditional expression, the condition is always followed by a question mark,
followed by the ‘then’ expression, followed by a colon, followed by the ‘else’ expression.
The overall value of the expression is the ‘then’ expression if the condition evaluates to
true, and the ‘else’ expression otherwise.
Another form of conditional statement is when we want to compare a value against a
number of different possibilities, and take action accordingly. This is facilitated by the
switch statement. To show its use, consider the following class.
<jag domain="doc/code/chap2">
class Calculator {
static real calc (char op, real val1, real val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/': return val1 / val2;
default:
sys.println("Unknown operator: ", op);
break;
}
return null;
}
static void main () {
sys.println(calc('+', 10, 20));
sys.println(calc('*', 10, 2.4));
}
}
</jag>
The calc() method takes three parameters: a character that represents an arithmetic
operator, and two real numbers. It uses a switch statement to determine what to do based
on the value of op. What appears after the switch keyword (within parentheses) is called a
selector. Each possibility is specified by a case, which consists of the keyword case,
followed by a literal (of the same type as the selector), followed by a colon, followed by
zero or more statements. The selector is compared against the case values, and the
statements for the matching case are executed. The final case (default) is optional and,
when present, acts as a ‘catch all’ – if none of the earlier cases match the selector then
this one is exercised instead.
Fundamentals
37
Running this program produces the following output.
30.0
24.0
Some further observations about this program:

Because calc() is a static method, it can be invoked without having an instance of the
class, as illustrated in main().

A character literal is always delimited by a pair of single quotes.

When a matching case of a switch is executed, execution continues to the statements
for the next case and so on, unless the switch is exited using a break statement, or the
whole method is exited using a return statement. The break statement at the end of
the default case is evidently redundant, but is considered good coding practice.
This program doesn’t cater for the possibility of division by zero. A call such as
calc('/', 10, 0) will result in the following.
…/code/chap2/Calculator.jag (line 10 col 25-26) ERROR: division by zero
10
case '/': return val1 / val2;
^
We can cater for this case using an if statement:
static real calc2 (char op, real val1, real val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/':
if (val2 == 0)
break;
return val1 / val2;
default:
sys.println("Unknown operator: ", op);
break;
}
return null;
}
It’s worth noting that the type of values you can use for a switch selector (and the literals
for the cases) must be integer, character, or symbol. We’ve already seen examples of
numbers, characters, and string literals. A symbol literal consists of the $ character
followed by an identifier. For example: $name, $address, $id.
Here is a revised version of the Calculator class using symbols instead of characters.
38
JavaGram Agile Development
static real calc3 (symbol op, real val1, real val2) {
switch (op) {
case $add: return val1 + val2;
case $subtract: return val1 - val2;
case $multiply: return val1 * val2;
case $divide:
if (val2 == 0)
break;
return val1 / val2;
default:
sys.println("Unknown operator: ", op);
break;
}
return null;
}
In many ways, symbols can be thought of as pseudo identifiers, which makes them useful
for storing identifiers in variables, objects, etc. For such uses, they’re superior to strings –
they consume less memory and they can be more efficiently compared. For example, if a
string (such as "name") appears 20 times in a program, JavaGram actually creates 20
separate representations of it in memory. Whereas if a symbol (such as $name) appears 20
times in a program, JavaGram creates only one instance of it, causing all occurrences to
point to the same representation.
To appreciate the comparison efficiency, consider the following cases.
"name" == "name"
$name == $name
// gives true
// gives true
The == operator checks for equality. Now consider the identicity operator.
"name" === "name"
$name === $name
// gives false (stored in separate places)
// gives true (stored in the same location)
This operator returns true only when its two operands point to the same thing. It’s exactly
this reason that makes symbols suitable for use in a switch.
2.6 Iteration
A common programming task is to repeat a certain computation many times (e.g.,
calculate the interest for each account held at a bank). This is called iteration and is
supported by various forms of loop statements.
As an example, let’s consider the task of calculating the factorial of a number. The
factorial of a number n (written as n!) is defined by these rules:

Factorial of 0 is 1.

Factorial of n is n times factorial of n-1.
Fundamentals
39
So, for example:
5! = 5 × 4 × 3 × 2 × 1 = 120
Here is a simple class that uses a loop to implement factorial.
<jag domain="doc/code/chap2">
class Factorial {
public static int factorial (int n) {
int f = n == 0 ? 1 : n;
while (n > 1) {
f *= n - 1;
n -= 1;
}
return f;
}
public static void main () {
sys.println("factorial(5) = ", factorial(5));
}
}
</jag>
The while loop in the factorial() method iterates through decreasing values of n. The
loop condition (n > 1) is first evaluated. If this condition evaluates to true then the loop
body (appearing within a pair of braces) is executed. Otherwise, the loop is terminated.
Each time round the loop, f is multiplied by the next value (n - 1) and n is decremented
in preparation for the next iteration.
Running this program produces the following output.
factorial(5) = 120
Incrementing and decrementing an integer are such common tasks that there are specific
operators for them: ++ (auto increment operator) increments an integer by 1 and -- (auto
decrement operator) decrements an integer by one. We can use the auto decrement
operator to write factorial() more succinctly:
public static int factorial2 (int n) {
assert n >= 0;
int f = n == 0 ? 1 : n;
while (n > 1)
f *= --n;
return f;
}
The auto increment/decrement operator can appear before or after an lvalue. If it appears
before (as in the above example), the lvalue is incremented/decremented first and then its
40
JavaGram Agile Development
resulting value is used. Conversely, if it appears after, the lvalue is used and then it’s
incremented/decremented.
Also note that, because the while loop now has a single statement as its body, there is no
need to use braces to enclose it.
Finally, we’ve added an assert statement to the beginning of the method to guard against
situations where n is negative. An assert is always followed by a logical condition. If the
condition evaluates to false, an error is raised.
There is a variant of the while loop called the do-while loop. It’s useful for situations
where we want the loop body to execute at least once. Unlike the while loop, a do-while
loop first executes the loop body and then evaluates the loop condition. Here is a simple
class that illustrates its use.
<jag domain="doc/code/chap2">
class Prime {
public static boolean isPrime (int n) {
assert n > 0;
int i = 2;
do {
if (n % i == 0)
return false;
} while (++i < n/2);
return true;
}
public static void main () {
sys.println("isPrime(13) = ", isPrime(13));
sys.println("isPrime(111) = ", isPrime(111));
}
}
</jag>
The method isPrime() returns true when its parameter (n) denotes a prime number. A
prime number is one that’s divisible only by itself and 1. We’ve used a very simple
algorithm here which iterates i from 2 to n/2, and checks to see if n is divisible by i. The
latter is done using the remainder operator (%) which gives the remainder of dividing n by
i.
Note how we’ve declared the return type of the method to be boolean. This type covers
only two possible values: true and false.
When run, the program produces the following output.
isPrime(13) = true
isPrime(111) = false
Fundamentals
41
The final form of loop to be discussed here is the for loop. We can rewrite the isPrime()
method more succinctly using this loop.
public static boolean isPrime2 (int n) {
assert n > 0;
for (int i = 2; i < n/2; ++i) {
if (n % i == 0)
return false;
}
return true;
}
The parentheses appearing after the for keyword can accommodate three parts (separated
by semicolons) every one of which is optional. The first part is performed only once
when the loop commences execution. This is typically an assignment, but more
commonly a locally declared variable that’s initialized to a value (as is the case in the
above loop). The second part is the loop’s logical condition. The last part is a step that’s
performed at the end of each iteration (before the loop condition is re-evaluated).
Note that because i is declared as the loop’s local variable, it remains only visible within
the loop, so it cannot be referred to outside the loop.
The simplest form of for loop is one where all the optional parts are missing.
for (;;) {
//...
}
The effect of this loop is to iterate indefinitely; it’s equivalent to the following while
loop.
while (true) {
//...
}
2.7 Composites
The term composite refers to a data structure that can hold multiple values. By contrast,
simple data items (such as numbers, strings, symbols) are called atomic because they
cannot be broken down into smaller things. We’ve already seen one form of composite
data: class instances. The second form is called containers.
2.7.1 Containers
Containers are exceptionally useful for everyday programming, so much so that
JavaGram has built-in notations to support them.
There are three different types of containers:
42
JavaGram Agile Development

A list is an ordered sequence of items. Internally, a list is implemented as a linked list
of items (i.e., each item has a pointer to the next item in the list). This makes lists
unsuitable for random access because, in order to get to an item, you have to iterate
through the earlier items in the list. The advantage of lists, however, is that they carry
very little storage overheads. That makes them ideal for reasonably short sequences
(i.e., up to tens of items).

A vector is also an ordered sequence of items but with the added advantage of
random access. Given the zero-based position of an item in the vector, you can look it
up directly. Vectors are especially suitable for large data sets that require random
and/or frequent access.

A map is a collection of items where each item (value) is accessed through a key. The
items are maintained in ascending lexicographic key sort order. Conceptually, you
can think of each entry in the map as a key/value pair. Maps support random access –
given a specific key, you can look up the corresponding value.
Containers are very flexible in that the items in a container can themselves be containers.
So, for example, you can have a map whose keys are symbols and whose values are
vectors, or a vector whose items are themselves vectors, and so on.
You would have noticed that with the atomic types, you can specify a value either
literally (e.g., 10) or programmatically (e.g., i + 10). The same is true of container types.
2.7.2 Lists
All lists are of type list. Here is a simple list of three numbers:
list nums = $(10, 20, 30);
List literals are always delimited by parentheses, and the individual items are separated
by commas. However, if a list literal is not inside another literal, its first parenthesis must
be preceded by a $ (as in the above example) to avoid ambiguity.
To access the items in a list, you can use the sys.head() and sys.tail() methods. For
example:
sys.head(nums)
sys.tail(nums)
sys.head(sys.tail(nums))
// gives 10
// gives (20, 30)
// gives 20
Alternatively, you can use the [] operator to access list members. For example:
nums[1]
// gives 20
You can also create a list dynamically using the sys.pair() method. For example:
list nums = sys.pair(10, sys.pair(20, sys.pair(30, null)))
Fundamentals
43
The inner-most call, sys.pair(20, null), creates the list (30) and the outer calls add 20
and then 10 in front of this list to produce (10, 20, 30). A much easier way of doing the
same is to use the list() operator:
list nums = list(10, 20, 30);
As a rule, the end of a list is always marked by a null (which is also a shorthand for an
empty list). Also, for programming convenience, applying sys.head() or sys.tail() to
null will produce null.
The elements of a list need not be of the same type – they can be anything. For example:
list ls = $(1, "man", "bites", ($animal, "dog"), 3, "times")
Note how the inner list has no preceding $, because it appears inside another literal.
2.7.3 Vectors
The basic type for a vector is vector. Here is a simple vector literal of three numbers:
vector nums = [10, 20, 30]
Vector literals are always delimited by square brackets, and the individual items are
separated by commas. As with lists, the elements of a vector need not be of the same
type, but you can enforce a specific element type where desirable. For example, the above
list is better written as
vector<int> nums = [10, 20, 30];
because it explicitly states that the elements are expected to be of type int. In this case,
should the vector contain a non-integer element, an error will be raised.
You can access an element of a vector using its zero-based index. For example:
nums[1]
// gives 20
The sys pseudo class provides a number of methods for inserting, removing, and finding
vector elements, as exemplified below.
sys.insert(nums, 2, 25)
sys.append(nums, 40)
sys.find(nums, 30)
sys.remove(nums, 1, $at)
sys.member(nums, 30)
sys.length(nums)
sys.clear(nums)
44
//
//
//
//
//
//
//
inserts 25 at position 2
appends 40 to the end of the vector
returns the index of 30 (ie, 2)
removes the element at position 1
gives true
gives the number of elements in the vector
removes all the vector elements
JavaGram Agile Development
Let’s look at a real-life example that illustrates the use of both vector and list. The
following class is intended to capture directions in order to get from one location to
another, in the style: go 5km east, then 7km south, then 12km west, etc. It uses a vector of
lists (called legs) to capture the individual legs of a journey, where each leg is of the form
(direction, distance).
<jag domain="doc/code/chap2">
class Direction {
vector<list> legs @= vector();
public void addLeg (symbol dir, real dist) {
sys.append(legs, list(dir, dist));
}
public real totalDistance () {
real total = 0;
for (int i = 0, n = sys.length(legs); i < n; ++i)
total += legs[i][1] @ real;
return total;
}
public real shortestDistance () {
real east = 0;
real north = 0;
for (list leg in legs) {
real dist @= leg[1];
switch (leg[0]@symbol) {
case $north: north += dist; break;
case $south: north -= dist; break;
case $east: east += dist; break;
case $west: east -= dist; break;
}
}
return sys.sqrt(east * east + north * north);
}
public static void main () {
Direction d = new Direction();
d.addLeg($north, 12.5);
d.addLeg($west, 7.2);
d.addLeg($south, 2.6);
d.addLeg($east, 20.1);
sys.println("Total distance = ", sys.format(d.totalDistance(), "0.00km"));
sys.println("Shortest distance = ", sys.format(d.shortestDistance(), "0.00km"));
}
}
</jag>
The method addLeg() adds a new leg to the journey by appending a new list to the end of
the legs vector. The method totalDistance() uses a for-loop to iterate through the legs
vector and adds up the distance of each leg. A couple of points worth noting about this
method:
Fundamentals
45

Note how two (comma-separated) local variables are defined for the for-loop. This is
a useful coding pattern to remember as it avoids the length of the vector being
calculated for each iteration, which would have been the case, had it been coded as:
for (int i = 0; i < sys.length(legs); ++i).

When adding the leg’s distance to total (inside the loop) the value is first converted
to real using the type cast operator @. This is necessary because a list’s elements are
un-typed.
This is a good time to introduce the for-in loop – a variant of the for-loop that’s
particularly well suited to iterating through a container. Using it, totalDistance() can be
written more elegantly:
public real totalDistance2 () {
real total = 0;
for (list leg in legs)
total += leg[1] @ real;
return total;
}
A for-in loop must always have a local variable (e.g., leg) and a container (e.g., legs).
The latter must be a vector, map, or GUI container. Each time round the loop, the
variable points to the next container element.
The shortestDistance() method calculates the shortest distance between the start and end
points. It uses a for-in loop and a switch to work out how far (in total) we’ve moved north
and east, and then uses the Pythagoras theorem to calculate the shortest distance. The
sys.sqrt() method calculates the square root of a number.
The line before the switch uses the @= operator to cast the right side of the assignment to
the type expected by the lvalue on the left side, before performing the assignment. This is
more elegant than writing:
real dist = leg[1] @ real;
When executed, the program produces the following output.
Total distance = 42.400000000000006
Shortest distance = 16.260996279441187
These numbers look a little bit ugly (too many decimal places) and have no unit. We can
use the sys.format() method to limit them to, say, two decimal places and express them
in kilometers:
sys.println("Total distance = ", sys.format(d.totalDistance(), "0.00km"));
sys.println("Shortest distance = ", sys.format(d.shortestDistance(), "0.00km"));
46
JavaGram Agile Development
This will change the output to something more user friendly:
Total distance = 42.40km
Shortest distance = 16.26km
2.7.4 Maps
The basic type for a map is map. Here is a simple map that associates peoples name with
their age:
map age = ["Adam"=>20, "Paul"=>28, "Linda"=>18];
Map literals are always delimited by square brackets, and the pairs are separated by
commas. The keys (e.g., "Adam") and values (e.g., 20) of a map need not be all of the same
type, but you can enforce a specific key and/or value type where desirable. For example,
the above map is better written as
map<string,int> age = ["Adam"=>20, "Paul"=>28, "Linda"=>18];
because it explicitly states that the keys are expected to be of type string and the values
of type int. In this case, should the map contain any other type of key or value, an error
will be raised.
You can look up a value in a map using its key. For example:
age["Linda"]
// gives 18
The same notation can be used to store a key/value pair in a map, or to overwrite an
existing one:
age["Linda"] = 19
age["Jane"] = 30
// changes Linda’s age to 19
// adds a new key/value pair
The keys in a map must be atomic, but the values can be anything. If you want to specify
the type of keys or values but not the other, use the vague type (which stands for an
unspecified type). For example:
map<symbol,vague> person = [$name=>"John", $age=>22, $male=>true]
In fact, the type map is equivalent to map<vague,vague> and vector is equivalent to
vector<vague>.
A map whose keys are symbols has some similarity to an object, so JavaGram allows you
to use the dot notation instead of [] to access it. For example:
person.$name
person.$name = "Alan"
Fundamentals
// same as: person[$name]
// same as: person[$name] = "Alan"
47
The sys pseudo class provides a number of methods for use with maps, as exemplified
below.
sys.remove(person, $age)
sys.length(person)
sys.member($age, person)
sys.mapKeys(person)
sys.mapValues(person)
sys.clear(person)
//
//
//
//
//
//
removes the $age key and its value
gives the number of elements in the map
gives true if the key is in the map
gives: [$age, $male, $name]
gives: [22, true, "John"]
removes all the map elements
Internally, maps are maintained in ascending lexicographic key sort order. So, for
example, if you print a map using sys.print(), the elements will appear in ascending key
sort order. Similarly, sys.mapKeys() returns the keys in ascending sort order.
Let’s look at a program that demonstrates the versatility of maps. The following class
scans the file hierarchy in a directory and, for each file, counts the number of lines,
words, and characters. It uses a map (counter) to keep track of these statistics for each
scanned file.
<jag domain="doc/code/chap2">
class WordCount {
map<string, map<symbol,int>> counter @= map();
public void scanDir (string path) {
for (string file in sys.listDir(path)) {
string filePath = sys.pathConc(path, file);
if (sys.pathProps(filePath)[$type] == $dir)
scanDir(filePath);
else
scanFile(filePath);
}
}
public void scanFile (string path) {
stream s = sys.open(path, "r");
string line;
int nLines = 0, nWords = 0, nChars = 0;
while ((line = sys.readln(s, false)) != null) {
++nLines;
nWords += countWords(line);
nChars += sys.length(line);
}
counter[path] = map($lines=>nLines, $words=>nWords, $chars=>nChars);
sys.close(s);
}
protected int countWords (string line) {
int n = 0;
for (string str in sys.strSplit(line, " ")) {
if (sys.length(sys.strTrim(str, true)) > 0)
++n;
48
JavaGram Agile Development
}
return n;
}
public void clear () {
sys.clear(counter);
}
public void output () {
vector<string> files @= sys.sort(sys.mapKeys(counter));
map<symbol, int> total = map($lines=>0, $words=>0, $chars=>0);
for (string file in files) {
map<symbol, int> count = counter[file];
sys.println($"{file}: lines: {count[$lines]}, words: {count[$words]},
chars: {count[$chars]}");
for (symbol s in total)
total[s] += count[s];
}
sys.println($"Total: lines: {total[$lines]}, words: {total[$words]},
chars: {total[$chars]}");
}
public static void main () {
WordCount wc = new WordCount();
wc.scanDir(sys.pathConc(sys.root, "doc/code/chap2"));
wc.output();
}
}
</jag>
The scanDir() method takes the path of a directory as parameter and uses sys.listDir()
to get a list of all files/directories in that directory. sys.pathConc() is used to concatenate
the parent directory’s path with each file/directory name to produce an absolute path.
sys.pathProps() returns the properties of a path as a map which contains, amongst other
things, a $type key that points to $file or $dir, depending on whether it’s a file or
directory. For a directory, we call scanDir() recursively; and for a file, we call
scanFile().
To scan a file, scanFile() opens the file using sys.open(). The second argument to this
method ("r") stands for ‘read mode’. This method returns a stream (a built-in JavaGram
type that can be used for I/O with respect to files, buffers, channels). sys.readln() is used
to read the next line of the file, which it returns as a string. The second argument to this
method indicates whether the end-of-line (EOL) character should be included (set to
false to exclude EOL). When we reach the end of the file, this method returns null. For
each line, we increment three local counter variables, which are then used after the loop
to add a new map to the counter map. Finally, we close the stream using sys.close().
countWords() counts the number of words in a line. We’ve used a very simple algorithm
here: sys.strSplit() splits the line into its space-separated sub-strings. A sub-string is
considered a word if after trimming it of blanks it remains non-empty.
Fundamentals
49
The output() method formats and writes the result of a scan (as denoted by the counter
map) to standard output. The files are first sorted in alphabetic order using sys.sort(). A
for-loop is then used to iterate through the files, outputting the counters for each file and
at the same time building up totals in the totals map.
For outputting the file counters as well as the total counters, we’ve used delayed strings.
A delayed string is like a normal string but is preceded by a $ character. For example:
$"{file}: lines: {count[$lines]}, words: {count[$words]}, chars: {count[$chars]}"
Within a delayed string, anything enclosed by a pair of braces is treated as an expression.
When the delayed string is evaluated, these expressions are individually evaluated and
their values are spliced into the string.
Running this program will produce output similar to the following.
C:/JavaGram/doc/code/chap2/BankAcc.jag: lines: 27, words: 91, chars: 646
C:/JavaGram/doc/code/chap2/Calculator.jag: lines: 53, words: 173, chars: 1092
C:/JavaGram/doc/code/chap2/Car.jag: lines: 16, words: 53, chars: 296
C:/JavaGram/doc/code/chap2/CarTest.jag: lines: 13, words: 25, chars: 190
C:/JavaGram/doc/code/chap2/Direction.jag: lines: 45, words: 167, chars: 1152
C:/JavaGram/doc/code/chap2/Factorial.jag: lines: 24, words: 87, chars: 420
C:/JavaGram/doc/code/chap2/HelloWorld.jag: lines: 7, words: 16, chars: 115
C:/JavaGram/doc/code/chap2/Prime.jag: lines: 28, words: 96, chars: 557
C:/JavaGram/doc/code/chap2/WordCount.jag: lines: 55, words: 185, chars: 1481
Total: lines: 789, words: 1675, chars: 11467
2.7.5 Object Literals
An unusual feature of JavaGram is that it allows class instances to be specified as literals.
This is useful when you want to pre-create objects in code (load time) to represent things
such as meta data, rather than during execution (run time). Another benefit of this
approach is that it makes it very easy to serialize objects (in order to persist them to a file
or database) and to subsequently parse them (upon retrieval from the file or database). By
the same token, when you write an object to standard output, JavaGram outputs the object
as a literal, making it very easy to read and understand.
Recall the Direction class presented earlier in this chapter. You can use sys.println() on
the sample object to get its literal representation:
Direction d = new Direction();
//...
sys.println(d);
This will display the following:
[@doc\code\chap2\Direction legs=>[($north, 12.5), ($west, 7.2), ($south, 2.6),
($east, 20.1)]]
50
JavaGram Agile Development
Note the similarity to the map literal notation, except that:

The qualified class name appears at the beginning preceded by the @ character to
specify the object type. The reason for the qualified rather than short class name is
that this notation needs to be transportable.

Each object field (there is only one here: legs) is mapped to its literal value. Unlike
maps, however, the keys are the actual field identifiers, not literals.
Creating objects directly using this notation is easy. For example:
Direction dir = [@Direction legs=>[($west, 13.1), ($south, 1.9)]];
We’ve used the short class name here, assuming that the class domain is visible. Upon
parsing this code, JavaGram expands the short name to a qualified name. Just as vectors
and maps have special operators for dynamic creation, you can use the object operator to
create an object dynamically and directly. For example:
Direction dir = object(Direction, legs=>[($west, 13.1), ($south, 1.9)]);
If you want to get the string equivalent of an object, you can use the sys.serialize()
method. For example,
string str = sys.serialize(dir);
sets str to:
"[@doc\code\chap2\Direction legs=>[($west, 13.1), ($south, 1.9)]]"
Where explicit parsing is required (e.g., after reading an object literal from persistent
storage), you can use the sys.parse() method. For example,
sys.parse(str)
returns the original object. The Object library class (described in Chapter 13) uses this
approach for managing the persistence of business objects. See also the sample
application (in Chapter 8) for a real-life example of how persistent business objects are
utilized.
When writing an object literal, you can leave any of the class fields unspecified – these
are automatically set to null.
For efficiency and convenience, JavaGram uses a very direct method to manage the
creation of object literals. This means that class constructors (if any) are bypassed.
Therefore, if your constructors enforce important invariants or perform vital resource
management, these will not take place and remain your own responsibility.
Fundamentals
51
2.7.6 Literal versus Dynamic
As we’ve seen, composite data can be specified either as literals or created dynamically.
So what’s the difference?
The key difference is creation time. Literals are created when the code in which they
appear is loaded (i.e., ‘parse’ and ‘analyze’ steps, according to the diagram at the
beginning of this chapter). Dynamic data is created during the course of program
execution (i.e., ‘evaluate’ step in the same diagram). As a result, literals are created only
once, because their code is loaded only once, whereas dynamic data is created every time
the corresponding code is executed. This is a subtle difference that can be easily
overlooked by newcomers to JavaGram, with potentially dire consequences. It is
somewhat similar to programmers confusing the behavior of static and non-static data.
Both these forms have their legitimate place in JavaGram programming, so you must be
mindful of using the appropriate form for a given situation.
Let’s illustrate the difference (and consequences of misuse) using an example. Recall the
definition of the WordCount.output() method:
public void output () {
vector<string> files @= sys.sort(sys.mapKeys(counter));
map<symbol, int> total = map($lines=>0, $words=>0, $chars=>0);
//...
}
The total map in this method is created dynamically. What would happen if we change
this to a map literal?
public void output () {
vector<string> files @= sys.sort(sys.mapKeys(counter));
map<symbol, int> total = [$lines=>0, $words=>0, $chars=>0];
//...
}
Running this version will produce the same result as before. However, a subtle defect has
been introduced. If you call output() multiple times in the same run, the counters in total
will not start from zero, but will retain their value from the last call to output().
Remember, a literal is created once at load time, so in each call to output(), total is set
to refer to this same map, not a new copy, causing alterations made to it in the previous
call to be retained!
When literals are misused, the resulting defects might not be immediately obvious and
could require further testing to detect. Literals are generally safe and recommended for
these situations:

52
Read-only data
JavaGram Agile Development

Meta data

Persistence
2.8 Exception Handling
We’ve already seen how the assert statement can be used to impose invariants
(conditions that must hold). However, assertions have limited flexibility in that any
violation is reported as an error, with no further opportunity to handle the error.
Therefore, its use should be limited to scenarios where you want to guard against blatant
misuse.
There are other potential error situations that do not represent misuse but rather possible
set of circumstances that deserve to be detected and gracefully handled. These are called
exceptions. A simple example of this would be dealing with an invalid postcode in a
postal address. JavaGram provides an exception handling facility for such situations.
Let’s look at an example. Recall the WordCount.scanFile() method from an earlier
example in this chapter. This method does not cater for a potential error situation: what if
the file is not accessible for reading? In this case, the method sys.open() will fail, causing
a runtime failure of the program. To cater for this situation, we can rewrite the method as
follows.
public void scanFile2 (string path) {
stream s = null;
try {
s = sys.open(path, "r");
string line;
int nLines = 0, nWords = 0, nChars = 0;
while ((line = sys.readln(s, false)) != null) {
++nLines;
nWords += countWords(line);
nChars += sys.length(line);
}
counter[path] = map($lines=>nLines, $words=>nWords, $chars=>nChars);
}
catch (Exception e) {
sys.println(sys.err, "Can't read file: ", path);
}
finally {
if (s != null)
sys.close(s);
}
}
We’ve used a try-catch-finally statement to detect such an exception and deal with it.
The effect of this is that the statements in the try block are evaluated. If an exception
arises, the catch blocks following it are examined. The first catch block whose exception
type matches the raised exception is executed. The finally block is executed last,
Fundamentals
53
regardless of whether an exception arises or not, and even if a return statement is
executed.
Note how we’ve set the stream s to null before we enter the try block. If the sys.open()
call succeeds then s gets set to a valid stream. Otherwise, an exception is thrown and s
will remain null. Therefore, the finally block will only attempt to close the file if s is not
null. This simple code pattern ensures that there is no possibility of this method leaving
the file open due to an error.
Within the catch block, we simply report the fact that the file is not readable by writing a
message to standard error. Note how we’ve passed sys.err as the first argument to
sys.println(). The former is a predefined stream and denotes the standard error stream.
JavaGram provides three predefined streams:

sys.out denotes the standard output stream (usually associated with the screen).

sys.err denotes the standard error stream (also usually associated with the screen).

sys.in denotes the standard input stream (usually associated with the keyboard).
In our earlier uses of sys.println(), we never referred to any of these. That’s because
when no stream in specified, output methods such as sys.println() assume the standard
output stream. So, for example:
sys.println("Hello")
// is equivalent to: sys.println(sys.out, "Hello")
A catch block must always have a signature similar to a method of one parameter, which
must be of type Exception or a sub-class of Exception (sub-classes are described in the
next chapter).
Exception is a predefined JavaGram class, having the following definition.
class Exception {
protected string message;
public Exception () {}
public Exception (string msg) {message = msg;}
public string getMessage () {return message;}
}
As well as the exceptions raised by JAG (e.g., division by zero), the programmer can
detect error situations and throw an explicit exception. To do this, create an instance of
Exception and throw it, for example, like this:
throw new Exception("invalid postcode");
The general rule is that when a method throws an exception, it’s the responsibility of the
calling code to catch and deal with that exception.
54
JavaGram Agile Development
Once an exception has been caught by a catch block, it doesn’t propagate any further.
However, sometimes it makes sense to do something in a catch block in response to an
exception and then to propagate it by throwing it again. For example:
catch (Exception e) {
// Do something
throw e;
}
In general, the catch block and the finally block of a try statement are optional – either
may be absent but not both! Also, you can have multiple catch blocks, each responsible
for catching and handling a different type of exception. We’ll discuss this further after
we’ve introduced class inheritance in the next chapter.
Fundamentals
55
3 Object-oriented Programming
Classes were introduced in the previous chapter. This chapter builds on that foundation
and describes how classes can be extended to take advantage of object oriented (OO)
features such as inheritance and polymorphism. Simple programming examples are used
to illustrate the application of these concepts.
3.1 Inheritance
Think of a program that deals with geometric shapes – lines, rectangles, ovals, polygons,
etc. Each of these can be represented by a class. Many methods would be common to all
these classes, such as: draw(), move(), resize(), fill(). Simplistically, we can implement
each shape using a separate class, but later on when we start using these classes, a
recurring annoyance emerges: in order to do something to a shape, we need to know what
type of shape it is. For example, suppose that we have a Canvas class that can
accommodate multiple shapes, having a draw() method that draws the whole canvas by
drawing each shape:
class Canvas {
vector<object> shapes @= vector();
//...
public void draw () {
for (object shape in shapes) {
switch (typeof(shape)) {
case $Line: [email protected](); break;
case $Rectangle: [email protected](); break;
case $Oval: [email protected](); break;
case $Polygon: [email protected](); break;
}
}
}
}
There are three problems with this design:

The coding of Canvas methods, such as draw(), becomes quite tedious, requiring a
switch to handle each shape type separately.

Adding a new type of shape (e.g., PolyLine) will require changes to many of the
Canvas methods to cater for it. This could involve significant effort and is potentially
error prone.

There are likely to be fields and methods with identical definition for each shape
(e.g., color and getColor()). These would need to be redefined for every shape class,
resulting in unnecessary duplication of code.
56
JavaGram Agile Development
Inheritance provides an elegant solution to this problem, allowing us to write the common
parts once and override those methods that are specific to each class.
When designing classes, it’s useful to visualize the class hierarchy using a graphical
notation such as UML. The following UML diagram shows how we can organize our
shape classes to take advantage of inheritance.
Shape
-color: string
Canvas
shapes
+draw (): void
+draw(): void
+move(x:int, y:int): void
+resize(x:int, y:int): void
+fill(color:string): void
+getColor(): string
+setColor(color:string): void
Line
Rectangle
Oval
Polygon
+draw (): void
+move(x:int, y:int): void
+resize(x:int, y:int): void
+draw (): void
+move(x:int, y:int): void
+resize(x:int, y:int): void
+fill(color:string): void
+draw (): void
+move(x:int, y:int): void
+resize(x:int, y:int): void
+fill(color:string): void
+draw (): void
+move(x:int, y:int): void
+resize(x:int, y:int): void
+fill(color:string): void
In UML, each class is represented by a box divided into three parts – the top part bears
the class name, the middle part lists the class fields, and the bottom part lists its methods.
Shape is an abstract class (hence appearing in italics). Abstract classes cannot be
instantiated, but rather serve as design elements that other classes can extend. The
directed line from the Canvas class to the Shape class represents aggregation, implying
that Canvas can refer to multiple shapes. The directed line from each of the bottom classes
to the Shape class represents inheritance, implying that these classes inherit members
(fields and methods) from the Shape class. Because the bottom classes can be instantiated,
they’re said to be concrete.
In this diagram, Shape is said to be a base class (also called a super class). Each of Line,
Rectangle, Oval, and Polygon is said to be a derived class (also called a subclass).
The symbol appearing before each member denotes its visibility:

- stands for private

# stands for protected

+ stands for public
The methods draw(), move(), and resize() are defined as abstract in Shape (appearing in
italics). This means that their definition is deferred to a derived class. Each of the four
derived classes provides its own implementation of these methods. The remaining
Object-oriented Programming
57
methods and fields of Shape are inherited ‘as is’ by the derived classes, except for fill().
The latter is overridden by the 2D shapes and ignored by Line.
Given this design, Canvas can be defined much more elegantly.
class Canvas {
vector<Shape> shapes @= vector();
//...
public void draw () {
for (Shape shape in shapes)
shape.draw();
}
}
Because Shape is the base class for all shapes, we can draw the canvas by simply iterating
through its shapes and invoking each shape’s draw() method without needing to know
what type of shape it is. JavaGram resolves the call shape.draw() at runtime and invokes
the draw() method of the relevant concrete class. Therefore, draw() is said to be a
polymorphic method.
3.2 Shopping Cart Example
This section describes a fairly complete example of how inheritance can be used to
develop an OO solution to a problem. The problem is one of developing a shopping cart
for an online store.
First we need a representation of a customer visiting the store. For this, we’ll use a
minimal class, as this is not the focus of our discussion.
class Customer {
protected getable string name;
protected getable string address;
//...
public Customer (string name, string address) {
this.name = name;
this.address = address;
}
}
The getable qualifier deserves some explanation. A common coding pattern is to define a
get method (e.g., getName()) for private and protected fields of a class that need to be
accessed by the class users. Rather than defining such a method explicitly, you can use
the getable qualifier to instruct JavaGram to define it implicitly. JavaGram generates the
following hidden method for the name field.
public string getName () {
return name;
}
58
JavaGram Agile Development
There is also a setable qualifier which generates a hidden method for setting the value of
a field (setable also implies getable, so you don’t need to specify both). If used on the
name field, JavaGram will also generate the following hidden method.
public void setName (string name) {
this.name = name;
}
The online store offers a range of products for sale. We’ll use an abstract class to
represents all products.
abstract class Product {
protected getable string id;
// Unique product ID
protected getable real price; // Unit price is dollars
protected getable real weight; // Unit weight in kilograms
abstract public string format ();
public void ship (int quantity) {
Catalog.singleton.ship(id, quantity);
}
}
The abstract qualifier appearing before the class definition marks Product as abstract,
implying that this class can’t be instantiated directly. Each product has a unique numeric
ID, a price (expressed in dollars), and a weight (expressed in kilograms). The format()
method is intended to produce a string representation of the product. This method is
defined as abstract and therefore has no implementation – the implementation is
deferred to subclasses. The ship() method causes a given quantity of the product to be
shipped. We’ll discuss the implementation of this method later on.
Let’s now consider some real products. The first one is called Gadget and represents a
manufactured piece of equipment (such as an iPod).
class Gadget extends Product {
protected getable string make;
protected getable string model;
protected getable int year;
// Year of manufacture
public string format () {
return $"{id} {make} {model} {year} @{sys.format(price, ShopCart.MONEY)}";
}
}
Note how Gadget is derived from Product, using the keyword extends. This causes Gadget
to inherit everything defined in Product. The fields defined in Gadget are in addition to the
fields defined in Product. Any methods defined in the subclass are either in addition to
the base class, or override the ones in the base class. In this case, we have one such
Object-oriented Programming
59
method, format(), which provides an implementation of the same abstract method in
Product. This method uses a delayed string to format its return value. The last expression
in this string uses sys.format() to format the price of a gadget (ShopCart.MONEY is a
defined constant in another class). This call effectively resolves to this:
sys.format(price, "$0,000.00")
The format string (the second argument) causes price to be formatted as a dollar figure to
two decimal places, with every three whole figures comma separated. For example,
12000.45267 will be formatted as $12,000.45.
The next product type is Book.
class Book extends Product {
protected getable string author;
protected getable string title;
protected getable string publisher;
protected getable int year;
protected getable string isbn;
// Year of publication
// ISBN
public string format () {
return $"{id} {author}, {title}, {publisher} {year}, ISBN {isbn} @{sys.fo
rmat(price, ShopCart.MONEY)}";
}
}
Like Gadget, Book is a subclass of Product and has a similar definition.
The third and final product type to discuss here is an electronic book. Because EBook is
really a book, we’ve defined it by subclassing Book. Furthermore, to capture its additional
behavior (i.e., the fact that it’s electronic and therefore downloadable), we’ve also
subclassed it from another class: Downloadable. This is called multiple inheritance.
class EBook extends Book, Downloadable {
public real getWeight () {
return 0.0;
}
public string format () {
return [email protected]() + [email protected]();
}
public void ship (Customer cust, int quantity) {
create(id, $"{cust.getName()}: {quantity} copies");
}
}
EBook overrides three methods from its base classes. getWeight() is overridden to return
zero, because an ebook is not a physical entity. Note how format() invokes the same
method from both base classes. The keyword super refers to a base class. However,
60
JavaGram Agile Development
because there are two base classes, we’ve also used the cast operator to indicate which
particular base class we’re referring to (this would be unnecessary if there were just one
base class). Finally, we’ve also overridden the ship() method so that the downloadable
file is generated by this method. The create() method is defined in the Downloadable
class.
mutual class Downloadable {
protected string url;
// URL to download from
public void create (string prodId, string watermark) {
url = Catalog.singleton.createDownloadableFile(prodId, watermark);
}
public string format () {
return url == null ? "" : $" ({url})";
}
}
The mutual qualifier means that this class will be treated just once, no matter how many
times it appears in a derivation hierarchy (more on this in the next section). The create()
method refers to another class, Catalog, defined below.
Having defined all our product types, we can now define the class that represents the
shopping cart.
class ShopCart {
public static final string MONEY = "$0,000.00";
protected static int lastCartId = 0;
// Last allocated cart ID
protected int cartId;
// Unique cart ID
protected map<string,int> items @= map(); // Product ID => quantity
protected Customer customer;
// The customer who owns the cart
protected real totalWeight;
// Total weight of cart items
protected real totalCost;
// Total cost of items
public ShopCart () {
cartId = ++lastCartId;
}
public void addItem (string id, int quantity) {
items[id] = quantity;
}
public void checkOut (Customer cust) {
customer = cust;
totalWeight = 0.0;
totalCost = 0.0;
for (string id in items) {
Product prod = Catalog.singleton.getProduct(id);
totalWeight += prod.getWeight();
totalCost += prod.getPrice() * items[id];
prod.ship(items[id]);
}
Object-oriented Programming
61
totalCost += shippingCost(totalWeight);
}
public static real shippingCost (real weight) {
return weight * 5.5;
}
public void displayInvoice () {
sys.println("Customer: ", customer.getName());
sys.println("Address: ", customer.getAddress());
sys.println("Order:");
int idx = 0;
for (string id in items) {
Product prod = Catalog.singleton.getProduct(id);
sys.print(++idx, ". ", prod.format(), " x ", items[id], ": ");
sys.println(sys.format(prod.getPrice() * items[id], MONEY));
}
sys.println("Shipping: ", sys.format(shippingCost(totalWeight), MONEY));
sys.println("Total: ", sys.format(totalCost, MONEY));
}
}
Each ShopCart instance is allocated a unique ID (cartId) that’s generated by incrementing
lastCartId. The items in the cart are captured by the items map, which maps selected
product IDs to their purchase quantity. The customer, totalWeight, and totalCost fields
are set during the check out process.
Items are added to the cart using the addItem() method. The checkout() method iterates
through the cart items, works out total weight and cost, and ships each item. The shipping
cost is calculated by the shippingCost() method, whose value is added to the total cost.
displayInvoice() outputs the customer details, selected items and their quantities and
cost, shipping cost, and total cost. Note the use of the polymorphic method format() in
this method. The design ensures that as future product types are added, there will be no
impact on the ShopCart class.
The Product and ShopCart classes refer to another class called Catalog. The latter provides
an up-to-date catalog of all products available in the online store.
singleton class Catalog {
protected static int lastDownloadId = 0;
protected string file;
protected map<string,Product> products @= map(); // Product ID => Product details
protected map<string,int> stock @= map();
// Product ID => quantity in stock
public Catalog (string file) {
this.file = file;
stream s = null;
try {
s = sys.open(file, "r");
vague data = sys.eval(sys.read(s));
62
JavaGram Agile Development
if (!(data instanceof vector<list>))
throw new Exception(file + " has invalid format");
for (list pair in data@vector<list>) {
Product prod @= pair[0];
int quantity @= pair[1];
products[prod.getId()] = prod;
stock[prod.getId()] = quantity;
}
}
finally {
if (s != null)
sys.close(s);
}
}
public void save () {
vector<list> data @= vector();
for (string id in products)
sys.append(data, list(products[id], stock[id]));
stream s = null;
try {
s = sys.open(file, "w");
sys.ppln(s, data);
}
finally {
if (s != null)
sys.close(s);
}
}
public void displayCatalog () {
sys.println(sys.length(products), " products in catalog:");
for (vague id in sys.sort(sys.mapKeys(products)))
sys.println(products[id].format(), " (", stock[id], " available)");
sys.println();
}
public Product getProduct (string id) {
return products[id];
}
public int inStock (string prodId) {
int quantity = stock[prodId];
return quantity == null ? 0 : quantity;
}
public void ship (string prodId, int quantity) {
if (inStock(prodId) < quantity)
throw new Exception(prodId + " short of stock");
stock[prodId] -= quantity;
}
public string createDownloadableFile (string prodId, string watermark) {
string url = $"https://www.acme.com/download/{prodId}_{++lastDownloadId}.pdf";
// TODO: create a PDF file for the product denoted by prodId, where
// each page is watermarked with the string denoted by watermark.
return url;
Object-oriented Programming
63
}
}
This class is defined as singleton – JavaGram will ensure that no more than one instance
of this class will exist in a running process. Singleton is a commonly used design pattern,
so JavaGram provides direct support for it. You can refer to the one-and-only instance of
a singleton class using the Class.singleton notation (e.g., Catalog.singleton). If no
instance exists yet, JavaGram will create one for you. Otherwise, it will just return the
existing instance. Most singleton classes either don’t have a constructor or have a default
constructor (i.e., one with no parameters). This enables JavaGram to create an instance
implicitly. Where a singleton class’s constructor requires parameters, the programmer
must create the instance explicitly. We’ll show later on how to do this for the Catalog
class.
In a real application, Catalog and ShopCart would store their data in a database. For
simplicity however, we’ve chosen to store the catalog data in a file. The path for this file
is passed to the constructor. The assumed format for the file is that it contains a vector of
lists, where each list consists of a product and its quantity in stock. The constructor reads
the data from this file, checks that it’s a vector and then iterates through the vector to
populate the products and stock maps. Note the use of sys.read() for reading the file
data. Unlike sys.readln() which reads data textually, line by line, sys.read() reads valid
JavaGram expressions. We pass this to sys.eval() to also perform any necessary
evaluations, though this is not really necessary in this case, as the data is expected to
consist of literals only.
The save() method writes the data back to the file, ensuring that any changes (new
products added, changed quantities) are permanently saved. The ship() method simply
reduces the stock quantity for a product as a result of a purchase. The
createDownloadableFile() method is intended to generate a PDF file for a given digital
product, where each page bears a watermark. It returns the URL of the generated file.
A sample catalog file is shown below to illustrate the intended format.
Catalog.jag
[$([@Gadget id=>"GM001", price=>249.0, weight=>0.14, make=>"Apple", model=>"
iPod classic 120GB", year=>2009], 10)
,$([@Gadget id=>"GM002", price=>399.0, weight=>0.115, make=>"Apple", model=>
"iPod touch 32GB", year=>2009], 10)
,$([@Gadget id=>"GM003", price=>149.0, weight=>0.0368, make=>"Apple", model=
>"iPod nano 8GB", year=>2009], 5)
,$([@Gadget id=>"GM004", price=>199.0, weight=>0.0368, make=>"Apple", model=
>"iPod nano 16GB", year=>2009], 12)
,$([@Gadget id=>"GM005", price=>79.0, weight=>0.0107, make=>"Apple", model=>
"iPod shuffle 4GB", year=>2009], 30)
,$([@Book id=>"PB001", price=>35.0, weight=>0.45, author=>"Peter Black", tit
le=>"Famous Gardens", publisher=>"Lighthouse", year=>2002, isbn=>"0-21217625-9"], 18)
64
JavaGram Agile Development
,$([@Book id=>"PB002", price=>40.0, weight=>1.04, author=>"Mary Adams", titl
e=>"Art of Sewing", publisher=>"Acme House", year=>2006, isbn=>"0-143-122312"], 15)
,$([@EBook id=>"EB001", price=>12.0, weight=>0.0, author=>"Jane Cornwall", t
itle=>"Child Psychology", publisher=>"Canyon", year=>2008, isbn=>"0-25419826-6"], 10)
]
The final class to describe in this section is a simple test driver for the earlier classes.
class OnlineStore {
static {
new Catalog(sys.pathConc(sys.root, "doc/code/chap3/Catalog.jag"));
}
public static void main () {
Catalog.singleton.displayCatalog();
ShopCart cart = new ShopCart();
cart.addItem("GM003", 1);
cart.addItem("PB001", 2);
cart.addItem("EB001", 1);
Customer cust = new Customer("John Smith", "5 Victory Dr, Blacktown, Wind
sor 768872-24");
cart.checkOut(cust);
cart.displayInvoice();
}
}
The static block defined in this class deserves some explanation. Sometimes there are
initializations that you want to perform before any class instance is created. A static block
provides a way of doing this. Code appearing in a static block is executed (only once)
when the class is loaded. You can have multiple static blocks in a class. These are
executed in the order in which they appear.
As states earlier, because Catalog is a singleton class whose constructor takes a
parameter, its instance must be created explicitly. The purpose of this static block is to do
just that. Note that we don’t need to assign this instance to any variable, because we can
subsequently refer to it as Catalog.singleton.
The main() method displays the catalog, creates a shopping cart, adds three items to it
from the catalog, adds a customer, performs a check out, and displays the invoice.
To summarize, the class hierarchy for this program is presented below as a UML
diagram.
Object-oriented Programming
65
OnlineStore
ShopCart
Catalog
-cartId: int
-items: map<string,int>
-totalWeight: real
-totalCost: real
-stock: map<string,int>
-file: string
-stock: map<string,int>
+Catalog(file:string)
+save(): void
+displayCatalog(): void
+getProduct(id:string): Product
+inStock(prodId:string): int
+ship(prodId:string, quantity:int): void
+createDow nloadableFile(prodId:string, w atermark:string): string
+addItem(id:string, quanitity:int): void
+checkOut(cust:Customer): void
+shippingCost(): real
+displayInvoice(): void
Customer
products
*
-name: string
-address: string
Product
-id: string
-price: real
-w eight: real
+format(): string
+ship(cust:Customer, quantity:int): void
Gadget
-make: string
-model: string
-year: int
+format(): string
Book
-author: string
-title: string
-publisher: string
-year: int
-isbn: string
+format(): string
«mutual»
Downloadable
-url: string
+create(prodId:string, w atermark:string): void
+format(): string
EBook
+getWeight(): real
+format(): string
+ship(quantity:int): void
When run, the program produces the following output.
8 products in catalog:
EB001 Jane Cornwall, Child Psychology, Canyon 2008, ISBN 0-254-19826-6 @$12.00 (0
available)
GM001 Apple iPod classic 120GB 2009 @$249.00 (10 available)
GM002 Apple iPod touch 32GB 2009 @$399.00 (10 available)
GM003 Apple iPod nano 8GB 2009 @$149.00 (5 available)
GM004 Apple iPod nano 16GB 2009 @$199.00 (12 available)
GM005 Apple iPod shuffle 4GB 2009 @$79.00 (30 available)
PB001 Peter Black, Famous Gardens, Lighthouse 2002, ISBN 0-212-17625-9 @$35.00
(18 available)
PB002 Mary Adams, Art of Sewing, Acme House 2006, ISBN 0-143-12231-2 @$40.00 (15
available)
Customer: John Smith
Address: 5 Victory Dr, Blacktown, Windsor 768872-24
Order:
66
JavaGram Agile Development
1. EB001 Jane Cornwall, Child Psychology, Canyon 2008, ISBN 0-254-19826-6 @$12.00
(https://www.acme.com/download/EB001_1.pdf) x 1: $12.00
2. GM003 Apple iPod nano 8GB 2009 @$149.00 x 1: $149.00
3. PB001 Peter Black, Famous Gardens, Lighthouse 2002, ISBN 0-212-17625-9 @$35.00
x 2: $70.00
Shipping: $2.68
Total: $233.68
3.3 Mutual Classes
Recall how the Downloadable class in the previous section was defined as mutual. This is
a necessary and important consideration when using multiple inheritance. There are two
basic rules regarding mutual classes:

Where a derived class has multiple base classes, at most one of them can be nonmutual.

All the base classes of a mutual class (if any) must also be mutual.
These rules allow JavaGram to layout the fields of a derived class instance in a
predictable manner and, at the same time, avoid duplication of field instances where a
class participates multiple times in a derivation hierarchy. Sounds confusing? Let’s
clarify the point using an example.
«mutual»
NetworkNode
-location
-bandw idth
«mutual»
«mutual»
Transmitter
Receiver
-carrier
-channels
Transceiver
Consider two mutual classes Transmitter and Receiver that extend another mutual class
NetworkNode. A Transceiver is a device capable of transmission and reception, so it’s best
defined as a derivation of Transmitter and Receiver. In OO programming, this kind of
class hierarchy is called the dreaded diamond problem, because it creates a problem for
the programming language in two respects:

How to layout the fields of an instance of Transceiver in memory, given that
NetworkNode’s fileds are effectively inherited twice.

How to resolve a call to a method that’s defined in both Transmitter and Receiver.
Object-oriented Programming
67
JavaGram addresses the first issue by ensuring that no matter how many times a super
class is inherited by a subclass, its fields will appear only once in an object of the
subclass. So in the above example, Transceiver will have four fields, not six.
To address the second problem, JavaGram requires that you explicitly cast to the intended
base class when making such calls (as was exemplified in the EBook.format() method of
the previous section).
3.4 Final Qualifier
Sometimes it’s desirable to prevent a class from being extended. Reasons may include:
security concerns, efficiency considerations, or design constraints. To do this, we simply
use the final qualifier when defining the class.
A simple example would be a User class in a security module that controls access to a
system.
final class User {
//...
public void login (string username, string password) {
//...
}
}
The intent here is to prevent someone from subclassing User and overriding the login()
method in order to avoid authentication.
This qualifier can also be used at a method level, so a similar way of enforcing the above
measure would be to define the class as:
class User {
//...
public final void login (string username, string password) {
//...
}
}
In this case, the class can be extended but the login() method can’t be overridden.
3.5 This and Super
When dealing with classes and inheritance, two keywords can come in handy: this and
super. We’ve already seen instances of their use in earlier examples, so we’ll just recap
their role and use here. Because both these refer to an instance of a class, it would be
meaningless to try to use them in static methods.
In the implementation of a non-static method, we can use this to refer to the implicit
class instance on which the method is invoked. We typically do this to avoid ambiguity.
68
JavaGram Agile Development
The most common case is when you have a method parameter or local variable that has
the same name as a class field. By using the this.field notation, we avoid this ambiguity
and make our intention clear. Another less common use is to invoke a constructor from
another constructor. For example, in
class Point {
int x, y;
public Point () {
this(0, 0);
}
public Point (int x, int y) {
this.x = x;
this.y = y;
}
}
The first constructor uses this to call the second constructor, and the second constructor
uses this to overcome the ambiguity of fields and parameters having the same name.
The super keyword is only meaningful in a derived class, and can be used to refer to a
base class. When there is only one base class, the intention is clear. However, when there
are multiple base classes, explicit casting must be used to overcome ambiguity.
Earlier we saw an example of this in the EBook.format() method:
public string format () {
return [email protected]() + [email protected]();
}
Because EBook has two base classes, both of which have a format() method, we’ve used
explicit casting after super to nominate the desired class to which the call should resolve.
3.6 Method Parameters
Sometimes it makes sense to define more than one ‘flavor’ of the same method in a class.
We saw an example of this in the previous section for the Point class, where two
constructors are provided. This is called method overloading and can be applied to
constructors as well as any other method. Here is another example:
class DataCache {
map<symbol,vague> cache @= map();
public void add (symbol key, vague data) {
cache[key] = data;
}
public void clear () {
sys.clear(cache);
}
public void clear (symbol key) {
Object-oriented Programming
69
sys.remove(cache, key);
}
public void clear (vector<symbol> keys) {
for (symbol key in keys)
sys.remove(cache, key);
}
//...
}
This class provides three clear() methods – one that clears the entire cache, another that
clears a specific item from the cache, and a third that clears multiple items from the
cache.
Another way of making a method more versatile is to give it default arguments. For
example, the two constructors for the Point class can be written more elegantly as one:
class Point {
int x, y;
public Point (int x = 0, int y = 0) {
this.x = x;
this.y = y;
}
}
Given this definition, the following constructor invocations are all valid:
Point p1 = new Point();
Point p2 = new Point(10, 20);
Point p3 = new Point(10);
// x == y == 0
// x == 10, y == 20
// x == 10, y == 0
In other words, where a trailing argument is not specified, the default value is used. As a
general rule, all default argument values must be trailing.
Similarly, we can combine the first two DataCache.clear() methods using a default
argument:
public void clear (symbol key = null) {
if (key == null)
sys.clear(cache);
else
sys.remove(cache, key);
}
However, this is not recommended as it will not improve the clarity of the code. The
recommended rule of thumb is to use the style that delivers the most clarity.
70
JavaGram Agile Development
3.7 Class Variables
An unusual but handy feature of JavaGram is that class names can be used as values, and
therefore assigned to lvalues or passed as arguments to methods. When you do this, the
corresponding lvalue must be of type vague. An actual example of this occurs in the
EventInitiator class of lib/lang/Event.jag standard library script, a snippet of which
appears below.
mutual class EventInitiator {
protected map<symbol, vector<EventListener>> eventMap @= map();
public void addListener (vague eventClassName, EventListener listener) {
symbol eventName = typeof(eventClassName);
vector<EventListener> listeners = eventMap[eventName];
if (listeners == null)
eventMap[eventName] = listeners @= vector();
sys.append(listeners, listener);
}
//...
}
Here, the first parameter of addListener() is intended to be an event class name. For
example, given a BindEvent class, we could write something like this:
EventInitiator editor;
EventListener screen;
//...
editor.addListener(BindEvent, screen);
Note how addListener() uses the typeof operator to convert the class name to a symbol.
This operator returns the qualified class name as a symbol.
You can also get the qualified name of a class as a symbol using the notation
ClassName.symbol (for example, Point.symbol). But note that typeof and .symbol serve
completely different purposes – the former operates on an expression, whereas the latter
is applied directly to a class name.
Finally, given an expression that evaluates to a class instance, you can use the notation
expr.class to get its class name. For example, if pt is of type Point then pt.class gives
Point as a vague value.
Object-oriented Programming
71
4 GUI Programming
Modern applications are generally expected to have a Graphical User Interface (GUI)
designed with intuitiveness and ease of use in mind. GUIs, however, tend to be code
intensive due to complexities such as event handling, data binding, the need for flexible
navigation paths, and so on. One of the design goals of JavaGram is to substantially
reduce this complexity by offering a declarative style of programming as opposed to the
procedural style of established GUI frameworks and libraries such as Java’s Swing.
JavaGram’s promise is that you’ll write a lot less code, your code will be far more
readable, and considerably easier to test, making the language particularly well-suited to
rapid prototyping and agile development.
4.1 Demo Application
To illustrate the many GUI elements to be covered in this chapter, we’ll use a demo
application that we’ll gradually build up and add elements to. There is no specific
business functionality behind this application other than showing how to code different
types of elements and manipulate their properties. The first cut of this application
(DemoApp.jag) is shown below.
<jag domain="doc/code/chap4">
<load>
"lib/gui/GuiApp"
</load>
singleton class DemoApp extends GuiApp {
<App app lookAndFeel=$windows>
<Frame frame title="Demo App" width=550 height=400 event=frameHandler/>
</App>
public DemoApp () {
super(frame);
}
protected void frameHandler (native comp, symbol event) {
if (event == $close)
exit();
}
public static void main () {
DemoApp.singleton.run();
}
}
</jag>
The easiest way to define a GUI application is to subclass the lib/gui/GuiApp library
class, which is what we’ve done here. Singleton classes come very handy when writing
GUI applications because many of the visual components tend to have singleton
behavior. We’ve defined DemoApp as a singleton for this reason.
72
JavaGram Agile Development
In JavaGram, GUI elements are defined using a markup notation. Such markups are
defined inside classes and behave like class fields. The <App> element represents a GUI
application. The identifier app names the element so that subsequently we can refer to the
element using this identifier. For elements that appear at the same level as class fields and
methods (called GUI class members) this is mandatory. For nested elements (such as
<Frame> in this example) this is optional, so we name these only when we actually need to
refer to them elsewhere in the code.
GUI class members can have any of the qualifiers allowed for fields, except for getable
and setable. For example, you can specify a GUI member to be protected static.
Each element type accepts a certain set of properties. These properties are typed and can
be set either at the time of defining the element or later on within the code of a method.
For <App>, for instance, we’ve set the lookAndFeel property to $windows. This causes the
application to assume a Microsoft Windows look and feel.
Many of the GUI elements are containers, allowing you to define your GUI as a
hierarchy. For example, we’ve defined <Frame> inside <App>. The former defines a main
frame for the application.
Some elements support event handlers. An event handler is a method with a
predetermined signature that gets called by JavaGram when the element receives certain
events. Event handlers are always defined using the event property, which is set to the
name of the method that handles the events (e.g., frameHandler() in the above example).
An event handler always takes two parameters: the comp parameter is set to the
component that has raised the event (in this case the frame itself), and the event
parameter is set to a symbol that represents the event (e.g., $close). The reason for having
the first parameter is that you can have multiple elements sharing the same event handler,
so you can examine comp to determine for which element the event has been raised. Our
eventHandler() here is quite simple – when the event is $close (which is raised when the
user clicks in the close box of the frame), it exists the application by calling the exit()
method of the base class.
GUI Programming
73
The main() method boots the application by calling the run() method of the base class,
causing the above to be displayed.
As we introduce other elements in the course of this chapter, feel free to look them up in
Chapter 11 to find out more about their properties. Element types form a hierarchy,
whereby one element type inherits the properties of another. As with classes, some
element types are abstract, so you can’t actually use them in your code – they serve as
abstractions for capturing common properties and behaviors. In general, however,
element types are not classes, so you should be mindful not to treat them as such.
4.2 Panels, Layouts, and Fields
Let’s extend our demo application by adding a tabbed pane to the main frame. For now,
we’ll add just one tab page that represents a person.
<App app lookAndFeel=$windows>
<Frame frame title="Demo App" width=550 height=400 event=frameHandler>
<Pane.tabbed tabs lay=$center>
<Tab title="Person" image={sys.use("lib/gifs/Person.gif")}>
<Indirect ref={PersonPanel.singleton.panel}/>
</Tab>
</Pane>
</Frame>
</App>
The <Pane.tabbed> element represents a tabbed pane. The dot in this element’s tag name
is a JavaGram convention that implies that there are a number of different pane types.
We’ll see another one called <Pane.scroll> shortly. Note how we’ve used </Pane> as the
closing tag. We could have equally used </Pane.tabbed>; the latter being the preferred
style for its superior readability. Like <Frame>, <Pane.tabbed> is a container. However, the
latter can only contain <Tab> elements.
74
JavaGram Agile Development
For our tab, we’ve specified both a title and an iconic image, both of which are optional.
The image property must be set to the absolute path of a GIF file. However, rather than
specifying the image path as a string, we’ve used sys.use() and passed the relative image
path to it (this path is relative to sys.root). This is the recommended coding style for
specifying image paths, because it ensures that the program will work correctly in both
standalone and client-server mode (introduced later in this book). sys.use() returns the
absolute path of the image file and, if necessary, downloads the image from a server.
Note how the call to sys.use() is enclosed in curly braces. In general, when you specify a
value for a property, it’s expected to be a literal. A non-literal value (e.g., an arbitrary
expression) must be enclosed in braces. This tells JavaGram that whatever is inside the
braces must be evaluated and the resulting value used instead.
Rather than coding the contents of the tab directly here, we’ve used an <Indirect>
element. This element is very useful when you want to spread the code for a GUI across
different classes. The ref property of this element must be set to the GUI component that
it represents which, in this case, is a panel specified in the singleton class PersonPanel.
<jag domain="doc/code/chap4">
singleton class PersonPanel {
static vector<string> STATES = [
"", "ACT", "NT", "NSW", "QLD", "SA", "TAS", "VIC", "WA"
];
<Panel panel type=Person>
<Layout.border/>
<Panel lay=$north>
<Layout.gridBag/>
<Lay row=0 col=0 weight=0.0 margin=2 align=$east>
<Label title="First Name"/>
</Lay>
<Lay row=0 col=1 fill=$horizontal margin=2>
<Field.text key=$firstName/>
</Lay>
<Lay row=0 col=2 weight=0.0 margin=2 align=$east>
<Label title="Last Name"/>
</Lay>
<Lay row=0 col=3 fill=$horizontal margin=2>
<Field.text key=$lastName/>
</Lay>
<Lay row=1 col=0 weight=0.0 margin=2 align=$east>
<Label title="Sex"/>
</Lay>
<Lay row=1 col=1 fill=$horizontal margin=2>
<Combo key=$sex data=["", "Male", "Female"]/>
</Lay>
<Lay row=1 col=2 weight=0.0 margin=2 align=$east>
<Label title="DOB"/>
</Lay>
<Lay row=1 col=3 fill=$horizontal margin=2>
GUI Programming
75
<Field.date key=$dob format="dd MMM yyyy"/>
</Lay>
<Lay row=2 col=0 weight=0.0 margin=2 align=$east>
<Label title="Occupation"/>
</Lay>
<Lay row=2 col=1 fill=$horizontal margin=2>
<Combo key=$occupation model=occupComboModel/>
</Lay>
<Lay row=2 col=3 fill=$horizontal margin=2>
<Option.tick title="Smoker" key=$smoker/>
</Lay>
</Panel>
<Pane.tabbed lay=$center>
<Tab title="Address">
<Panel key=$address type=Address>
<Layout.gridBag/>
<Lay row=0 col=0 weight=0.0 margin=2 align=$east>
<Label title="Street"/>
</Lay>
<Lay row=0 col=1 colSpan=3 fill=$horizontal margin=2>
<Field.text key=$street/>
</Lay>
<Lay row=0 col=4 weight=0.0 margin=2 align=$east>
<Label title="City"/>
</Lay>
<Lay row=0 col=5 fill=$horizontal margin=2>
<Field.text key=$city/>
</Lay>
<Lay row=1 col=0 weight=0.0 margin=2 align=$east>
<Label title="State"/>
</Lay>
<Lay row=1 col=1 fill=$horizontal margin=2>
<Combo key=$state data={STATES}/>
</Lay>
<Lay row=1 col=2 weight=0.0 margin=2 align=$east>
<Label title="Postcode"/>
</Lay>
<Lay row=1 col=3 fill=$horizontal margin=2>
<Field.text key=$postcode/>
</Lay>
<Lay row=1 col=4 weight=0.0 margin=2 align=$east>
<Label title="Country"/>
</Lay>
<Lay row=1 col=5 fill=$horizontal margin=2>
<Field.text key=$country/>
</Lay>
</Panel>
</Tab>
<Tab title="Comment">
<Pane.scroll>
<Area.text key=$comment/>
76
JavaGram Agile Development
</Pane>
</Tab>
</Pane.tabbed>
<Panel lay=$south>
<Button title="Bind to Map" image={sys.use("lib/gifs/Chain.gif")}
action={bindToMap()}/>
<Button title="Bind to Obj" image={sys.use("lib/gifs/Chain.gif")}
action={bindToObj()}/>
<Button title="Clear" image={sys.use("lib/gifs/Clear.gif")}
action={clear()}/>
<Button title="Map Binding" action={mapBinding()}/>
<Button title="Obj Binding" action={objBinding()}/>
</Panel>
</Panel>
public static final vector<string> OCCUPATIONS @= sys.sort([
"", "Engineer", "Scientist", "Accountant", "Teacher", "Manager",
"Administrator", "Health Worker", "Pilot", "Driver", "Mechanic",
"Public Servant", "Judge"
]);
// Just to illustrates the model style for combos.
protected vague occupComboModel (native combo, symbol cmd, int idx) {
switch (cmd) {
case $count: return sys.length(OCCUPATIONS);
case $get: return OCCUPATIONS[idx];
}
return "";
}
protected void bindToMap () {
map record = map(
$firstName=>"John", $lastName=>"Smith", $sex=>"Male", $dob=>[#1982-12-22],
$occupation=>"Mechanic", $smoker=>true, $comment=>"Sample comment",
$address=>[
$street=>"9 Grange St", $city=>"Balwyn", $state=>"VIC", $postcode=>"3103",
$country=>"Australia"
]
);
gui.bind(panel, record);
}
protected void bindToObj () {
Person record = [@Person
firstName=>"Linda", lastName=>"Forbes", sex=>"Female", dob=>[#1987-02-15],
occupation=>"Accountant", smoker=>false, comment=>"Another comment",
address=>[@Address
street=>"2 Smith St", city=>"Kew", state=>"VIC", postcode=>"3101",
country=>"Australia"
]
];
gui.bind(panel, record);
}
protected void clear () {
gui.bind(panel, map());
GUI Programming
77
}
protected void mapBinding () {
map record = map();
gui.save(panel, record);
sys.println(record);
}
protected void objBinding () {
Person record = new Person();
gui.save(panel, record);
sys.println(record);
}
}
</jag>
In this class, a person is represented by a <Panel> element. A <Panel> is by far the most
commonly-used container; it organizes its contents according to a specific layout. Where
a layout is specified, it should appear first inside the panel. If not specified, the panel
layout defaults to <Layout.flow>.
The top-level panel in the PersonPanel class is specified to have a border layout. This
layout allows you to organize the panel’s children according to the following diagram.
For each child, you can specify a lay property, set to one of the above values (defaults to
$center if unspecified). The unused parts of a border layout shrink to zero. Also, any
unclaimed space is usually taken up by $center.
In our example, we have a border-layout panel, where $north is used to display a person’s
details, $center is used to show address details and comment, and $south is used to
display a set of buttons, as illustrated below.
78
JavaGram Agile Development
The northern panel itself is specified to have a grid-bag layout. This layout is useful for
organizing a set of fields such that they are neatly aligned. Each child is laid out using a
<Lay> element, the properties of which determine where it’s placed, how it’s aligned, etc.
For specifying the fields, we’ve used the following elements:

<Label> for the text appearing to the left of each field.

<Field.text> for textual fields, such as first name.

<Field.date> for date fields, such as date of birth. Note the use of the format property,
which specifies the preferred format for data entry as well as display.

<Combo> for drop-down combo boxes, such as sex.

<Option.tick> for check boxes, such as smoker.
For elements, such as combo boxes, that can display a multitude of values, there are two
ways of specifying these values. The sex combo demonstrates the use of the direct data
approach, where the data property is set to the list of values to be displayed in the
combo’s drop down box. The occupation combo demonstrates the use of the data model
approach, where the model property is set to the name of a method in the same class that
provides this data. A combo’s model must have the following signature.
vague comboModel (native combo, symbol cmd, int idx)
JavaGram calls this method automatically, passing it appropriate arguments, whenever it
needs to obtain information on how to display the combo.
In the center of the panel, we’ve added a tabbed pane of two tabs – one for address details
and one for arbitrary comments. The former is organized using a grid-bag layout similar
to the person details panel. The latter uses two new elements:

<Area.text> for text boxes that can accommodate multiple lines of text.
GUI Programming
79

<Pane.scroll> to provide scrolling functionality for anything that can grow bigger
than the physical space it occupies.
The buttons in the south panel are for testing purposes. Each is specified as a <Button>
element, whose action property is evaluated when the button is pressed.
4.2.1 Data Binding
When a user is interacting with a GUI, two common patterns occur:

Data entry, whereby the user keys in some data into the fields of a screen. The
program then needs to transfer this data to some internal data structure, such as an
object, before it can do something useful with it.

Lookup, whereby the user retrieves information from, say, a database, and wants to
view it in a screen. Again, the data needs to be transferred from the program’s internal
data structure onto the screen fields.
In most GUI frameworks, these two tasks are the direct responsibility of the programmer
and may require much mundane coding. Although you can follow this same approach in
JavaGram, there is a much easier way.
You might have noticed that in the PersonPanel class, we’ve specified a key property for
each field that can hold a value. This property specifies the relationship between a GUI
element and its corresponding programmatic data. With this in place, you can bind an
entire screen to a corresponding data structure, leaving the field-level detail to JavaGram
to work out.
In JavaGram, the default data structure for binding is a map. However, you can also use
classes. If you refer back to the PersonPanel class, you’ll notice that both the person panel
and the address panel have their type property, respectively, set to class names Person and
Address. The minimal definition for these two classes is as follows.
class Person {
string firstName;
string lastName;
string sex;
date dob;
string occupation;
boolean smoker;
string comment;
Address address;
}
class Address {
string street;
string city;
string state;
string postcode;
string country;
80
JavaGram Agile Development
}
The key point to note is that the class field names match the key property of the
corresponding panel fields (albeit the latter are specified as symbols). In particular, note
how the address field of the Person class matches the key=$address property of the
address panel. In other words, class aggregation can be mirrored by panel nesting.
Now refer to the bindToObj() method which creates a Person object, whose address field
refers to an Address object. The gui.bind() call binds the whole person panel to the
person object, which then recursively binds the children (gui is a pseudo class – like sys –
that provides methods specific to GUI functionality). Pressing the Bind to Obj button
causes bindToObj() to be invoked and the binding to take effect, the result of which is
displayed below.
The objBinding() method does the opposite. It creates an empty Person object and
invokes gui.save() on the panel and this object, causing the panel data to be saved into
the object. Pressing the Obj Binding button causes objBinding() to be invoked, which
produces the following output.
[@Person address=>[@Address city=>"Kew", country=>"Australia", postcode=>"3101",
state=>"VIC", street=>"2 Smith St"], comment=>"Another comment", dob=>[#1987-0215], firstName=>"Linda", lastName=>"Forbes", occupation=>"Accountant",
sex=>"Female", smoker=>false]
It’s important to note that the inner Address object here is actually created by JavaGram
(as a result of the binding rules) and not the programmer.
The Bind to Map and Map Binding buttons, respectively, invoke bindToMap() and
mapBinding(). These have the same effect as the corresponding object buttons/methods
but use maps for data binding instead.
GUI Programming
81
Finally, the Clear button, clears all the panel data by binding the panel to an empty map.
An obvious question arises from this discussion: given the choice of objects and maps for
data binding, which one is recommended? All things considered, the answer depends on
the situation at hand. For example, suppose you have a search panel where you allow the
user to specify search criteria using a collection of fields. This is best served by an ad-hoc
data structure, so map binding would be ideal. On the other hand, if the search returns a
collection of objects (such as products) each of which is already an object, it’s best to use
these objects for the data binding of the screen that displays a product.
4.3 Trees
Trees are useful for visualizing hierarchical information, where there is parent-child
relationship. The root of a tree consists of zero or more nodes, where each node can
contain child nodes, and so on. A tree is defined using the <Tree> element and its nodes
are defined using the <Node> element.
As an example, consider a class that provides a tree view of a task hierarchy, such as how
to build a shed.
<jag domain="doc/code/chap4">
singleton class TaskTree {
<Icon icon image={sys.use("lib/gifs/Hammer.gif")}/>
<Tree tree event=treeHandler>
<Node title="Building a shed" image={icon}>
<Node title="Build foundation" image={icon}>
<Node title="Mark base and dig"/>
<Node title="Secure wire mesh"/>
<Node title="Pour concrete"/>
</Node>
<Node title="Build frame" image={icon} content="Shed.gif">
<Node title="Measure and cut timber"/>
<Node title="Erect and nail walls"/>
<Node title="Secure frame to foundation"/>
</Node>
<Node title="Install roof" image={icon}/>
<Node title="Install cladding" image={icon}/>
<Node title="Install door" image={icon}/>
</Node>
</Tree>
<Pane.split split divider=150 weight=0.3>
<Pane.scroll lay=$west>
<Indirect ref={tree}/>
</Pane.scroll>
<Panel blank lay=$east>
<Label title="No Detail"/>
</Panel>
</Pane.split>
<Pane.scroll picView>
82
JavaGram Agile Development
<Label picture/>
</Pane.scroll>
protected void treeHandler (native comp, symbol event) {
switch (event) {
case $select:
int oldDiv @= split.divider;
vague node = tree.select;
vague content = node@<Node>?.content;
if (content instanceof string) {
string path = sys.pathConc("doc/code/chap4", content@string);
picture.image = sys.use(path);
split.east = picView;
} else
split.east = blank;
split.divider = oldDiv;
break;
case $drill:
case $expand:
case $collapse:
break;
}
}
}
</jag>
Note how we’ve defined an <Icon> and reused it for specifying the image property of a
number of <Node> elements. We could have equally defined the node image properties
directly, but this approach is more convenient and efficient, because the image is created
only once and subsequently reused.
We’ve also used the <Pane.split> element to create a split pane, whose west side
contains the tree and whose east side is reserved for displaying the ‘content’ of each tree
node. The default content is a blank panel, which simply contains the label “No Detail”.
The alternative content is the picView scroll pane, which we’ll later use in the event
handler to display an image.
The interesting stuff happens in the tree event handling method treeHandler(). A tree can
generate four kinds of event:

$select is raised when a tree node is selected or deselected.

$drill is raised when a tree node is double-clicked.

$expand is raised when a parent node is expanded.

$collapse is raised when a parent node is collapsed.
In this example, we’re only interested in the $select event. Note how one of the tree
nodes (Build frame) has a content property. This property can be set to any arbitrary data
that we want to associate with a node. For example, in a CRM application, a tree node
GUI Programming
83
representing a customer could have its content set to the customer object. We’ve
programmed the handling of the $select event such that the currently selected node’s
content is displayed in the split pane’s east side.
In this example, we’re using an image file name as node content, with the intention that
when the node is selected, we’ll display this image in the east side. To do this, the event
handler gets the currently selected node using the tree select property. This property is of
type native because its underlying object (a tree node) is a Java Swing object. You’ll see
this type used extensively when writing GUI code. To get the node’s content property,
we use the notation node@<Node>?.content. The latter requires some explanation. Because
node is of type native, we must cast it to the correct GUI element type before we can
reference any of its properties. We do this using the notation node@<Node>. To get a
property, we usually use the dot notation. However, when a node is deselected, node ends
up being null, so to guard against this, we use the question-dot (?.) notation instead of
the dot notation. This is a JavaGram convenience that you can use when accessing a GUI
element’s properties or an object’s members. In other words,
vague content = node@<Node>?.content;
is equivalent to writing:
vague content = node == null ? null : node@<Node>.content;
If the content is a string then we treat it as a file name, work out its path, set the image
property of picture to it, and set the east side of the split pane to picView (this causes the
image to be displayed). Otherwise, we set the east side of split pane to blank.
When you change the content of a split pane, it may adjust the divider position to
accommodate what you’re displaying. To cancel the effect of this, we get the divider
property of the split pane first and restore it last.
Adding a tab for TaskTree to our demo app produces the following.
84
JavaGram Agile Development
4.3.1 Using a Tree Model
In the above example, we’ve used the direct data approach for specifying the tree’s
nodes. You also have the option of using a data model instead. The latter is useful when
the tree data is dynamic or so large that we don’t want to pre-create the nodes. For
example, if the data is sourced from a database and potentially large, it would be more
sensible to use a data model approach.
To specify a data model for a tree, set its model property to the name of the tree model
method (defined in the same class). Just to illustrate the approach, here is a revised
version of the TaskTree class that uses a tree data model.
singleton class TaskTree2 {
<Icon icon image={sys.use("lib/gifs/Hammer.gif")}/>
<Tree tree event=treeHandler model=treeModel>
</Tree>
<Pane.split split divider=150 weight=0.3>
<Pane.scroll lay=$west>
<Indirect ref={tree}/>
</Pane.scroll>
<Panel blank lay=$east>
<Label title="No Detail"/>
</Panel>
</Pane.split>
<Pane.scroll picView>
<Label picture/>
</Pane.scroll>
static map<int, map> tasks @= [
1=>[$name=>"Building a shed", $subs=>[2, 3, 4, 5, 6]]
,2=>[$name=>"Build foundation", $subs=>[7, 8, 9]]
GUI Programming
85
,3=>[$name=>"Build frame", $subs=>[10, 11, 12], $data=>"Shed.gif"]
,4=>[$name=>"Install roof"]
,5=>[$name=>"Install cladding"]
,6=>[$name=>"Install door"]
,7=>[$name=>"Mark base and dig"]
,8=>[$name=>"Secure wire mesh"]
,9=>[$name=>"Pour concrete"]
,10=>[$name=>"Measure and cut timber"]
,11=>[$name=>"Erect and nail walls"]
,12=>[$name=>"Secure frame to foundation"]
];
static int rootId = 1;
protected vague treeModel (native tree, symbol cmd, native node, vague subnode) {
switch (cmd) {
case $count:
return node == null ? 1 : sys.length(getSubs(node));
case $get:
int id = node == null ? 1 : getSubs(node)[subnode@int];
return getNode(id);
case $index:
return indexOfSubnode(node, subnode);
case $leaf:
vector subs = getSubs(node);
return subs == null || sys.length(subs) == 0;
}
return null;
}
protected vector<int> getSubs (native node) {
map content @= node == null ? tasks[rootId] : node@<Node>.content;
return (content == null ? null : content[$subs])@vector<int>;
}
protected native getNode (int id) {
map task = tasks[id];
if (task[$node] == null)
task[$node] = gui.create($Node, map($title=>task[$name], $content=>task,
$image=>icon));
return task[$node];
}
protected int indexOfSubnode (native node, native subnode) {
if (node != null) {
int idx = 0;
for (int id in getSubs(node)) {
if (tasks[id][$node] == subnode)
return idx;
++idx;
}
}
return 0;
}
protected void treeHandler (native comp, symbol event) {
switch (event) {
86
JavaGram Agile Development
case $select:
int oldDiv @= split.divider;
vague node = tree.select;
string file @= node@<Node>?.content@map?.$data;
if (file != null) {
string path = sys.pathConc("doc/code/chap4", file);
picture.image = sys.use(path);
split.east = picView;
} else
split.east = blank;
split.divider = oldDiv;
break;
case $drill:
case $expand:
case $collapse:
break;
}
}
}
The data that drives the model is denoted by the tasks static map. We’ve modeled this
such that it resembles data retrieved from a database. It maps each task ID to its
definition. For parent tasks, the latter contains a $subs key that points to a vector of its
children task IDs.
The tree model is denoted by the treeModel() method, which accepts four possible
commands:

$count requires the number of children of node to be returned.

$get requires the n-th child of node (as denoted by subnode as an integer index) to be
returned.

$index requires the zero-based index of subnode as a child of node to be returned.

$leaf requires true to be returned if node is a leaf node.
These are defined using three utility methods, which are self-explanatory, except for
getNode(). The latter returns a <Node> for a given task ID. New nodes are created
procedurally using the gui.create() method, which takes a GUI element name (as a
symbol) and a map that specifies its properties. A newly-created node is cached by
storing it in the task definition map under the $node key, so that we don’t need to recreate
it every time.
Finally, note how we’ve changed our approach to what we store in the content property
of each node – the corresponding task definition map. Therefore, treeHandler() is revised
accordingly.
GUI Programming
87
The above model-based tree produces exactly the same visual result as the earlier direct
data version.
4.4 Tables
Most applications must deal with tabular data (i.e., data items that conform to the same
structure such as objects of the same class, or maps sharing the same keys). A common
example is the result of a search. The <Table> element is ideal for visualizing tabular data.
Recall the Person class from an earlier section and suppose that we need a table to display
persons retrieved from a database.
<jag domain="doc/code/chap4">
<load>
"doc/code/chap4/PersonPanel"
</load>
singleton class PersonTable {
static final vector<map> TABLE_FORMAT @= [
[$key=>$firstName, $title=>"First Name", $width=>80, $align=>$west]
,[$key=>$lastName, $title=>"Last Name", $width=>100, $align=>$west]
,[$key=>$sex, $title=>"Sex", $width=>40, $align=>$west]
,[$key=>$dob, $title=>"DOB", $width=>80, $align=>$east, $format=>"dd MMM yyyy"]
,[$key=>$occupation, $title=>"Occupation", $width=>60, $align=>$west]
,[$key=>$smoker, $title=>"Smoker", $width=>40, $align=>$center]
];
static vector<Person> persons = [
[@Person firstName=>"John", lastName=>"Smith", sex=>"Male"
, dob=>[#1982-12-22], occupation=>"Mechanic", smoker=>true
],
[@Person firstName=>"Linda", lastName=>"Forbes", sex=>"Female"
, dob=>[#1987-02-15], occupation=>"Accountant", smoker=>false
],
[@Person firstName=>"Bob", lastName=>"Smart", sex=>"Male"
, dob=>[#1984-07-11], occupation=>"Teacher", smoker=>false
]
];
<Panel panel>
<Layout.border/>
<Pane.scroll lay=$center>
<Table table format={TABLE_FORMAT} data={persons} autoSize=true
event=tableHandler/>
</Pane.scroll>
<Panel lay=$south>
<Button title="Dump" action={dump()} enable={canDump()}/>
</Panel>
</Panel>
protected vague tableHandler (native comp, symbol event) {
switch (event) {
case $select:
gui.maintain(panel);
88
JavaGram Agile Development
break;
case $drill:
dump();
break;
case $hitCell:
break;
}
return null;
}
protected void dump () {
sys.ppln(persons[table.select@int]);
}
protected boolean canDump () {
return table.select != null;
}
}
</jag>
Like trees, tables can accept direct data or a data model. We’ve used the direct data
approach, for which we’ve created a static vector of Person objects. In a real application,
this data is likely to originate from a data source such as a database.
The table’s format property is set to the TABLE_FORMAT vector which specifies the format of
each column as a map whose structure should be self-explanatory. In the panel below the
table, we’ve created a button for dumping the data for the currently-selected row.
We’ve also specified an event handler method for the tree. A tree can raise three kinds of
event:

$select is raised when a row is selected or deselected. For this event, we’re calling
gui.maintain() on the whole panel so that JavaGram can update the visibility of the
components.

$drill is raised when a row is double-clicked. In response to this event, we dump the
row’s object to standard output by calling dump().

$hitCell is raised when a cell is clicked.
Adding this to the demo application gives us the following.
GUI Programming
89
Note how we’ve used $format for the DOB column to specify a preferred date format.
This approach is handy but of limited use. For example, it doesn’t give us a way of
changing the format of the Smoker column to use ticks instead of true/false. We’ll present
a more general approach shortly.
By default, the even and odd rows of a table are drawn with different background colors
to aid readability. You can change the row background colors by setting the bgColorOdd
and bgColorEven properties.
A table can be sorted by any given column. To sort in ascending column order, click on
the column heading. To sort by descending column order, shift-click on the column
heading. In either case, a small triangle appears in the column heading to indicate the sort
order. You can turn off sorting by setting the sortAscend or sortDescend property to null.
You can also control the sorting order by right-clicking on a cell or heading and choosing
from the resulting popup menu.
4.4.1 Using a Table Model
Let’s look at a revised version of the above example to illustrate a few more things – the
ability to edit cells, use of a data model, and more elaborate formatting of the cells.
singleton class PersonTable2 {
static <Field.date dateField/>
static <Option.tick smokerField align=$center enable=false/>
static <Combo occupCombo data={PersonPanel.OCCUPATIONS}/>
static <Icon icon image={sys.use("lib/gifs/Person.gif")}/>
static final vector<map> TABLE_FORMAT @= vector(
[$key=>$firstName, $title=>"First Name", $width=>70, $align=>$west, $icon=>true]
,[$key=>$lastName, $title=>"Last Name", $width=>80, $align=>$west]
,[$key=>$sex, $title=>"Sex", $width=>40, $align=>$west]
,map($key=>$dob, $title=>"DOB", $width=>80, $align=>$east,
90
JavaGram Agile Development
$format=>"dd MMM yyyy", $editor=>dateField)
,map($key=>$occupation, $title=>"Occupation", $width=>80, $align=>$west,
$editor=>occupCombo)
,map($key=>$smoker, $title=>"Smoker", $width=>40, $align=>$center,
$editor=>smokerField)
);
static vector<Person> persons = [
[@Person firstName=>"John", lastName=>"Smith", sex=>"Male"
, dob=>[#1982-12-22], occupation=>"Mechanic", smoker=>true
],
[@Person firstName=>"Linda", lastName=>"Forbes", sex=>"Female"
, dob=>[#1987-02-15], occupation=>"Accountant", smoker=>false
],
[@Person firstName=>"Bob", lastName=>"Smart", sex=>"Male"
, dob=>[#1984-07-11], occupation=>"Teacher", smoker=>false
]
];
<Panel panel>
<Layout.border/>
<Pane.scroll lay=$center>
<Table table format={TABLE_FORMAT} editable=true autoSize=true
styled=true model=tableModel event=tableHandler/>
</Pane.scroll>
<Panel lay=$south>
<Button title="Dump" action={dump()} enable={canDump()}/>
</Panel>
</Panel>
protected vague tableModel (native comp, symbol cmd, int row, int col) {
switch (cmd) {
case $rows:
return sys.length(persons);
case $cols:
return sys.length(TABLE_FORMAT);
case $get:
return persons[row][TABLE_FORMAT[col][$key]@symbol];
case $put:
persons[row][TABLE_FORMAT[col][$key]@symbol] = table.put;
break;
case $style:
return TABLE_FORMAT[col][$key] == $lastName ? [$bold] : null;
case $icon:
return TABLE_FORMAT[col][$key] == $firstName ? icon : null;
case $sortAscend:
case $sortDescend:
sys.sort(persons, cmd == $sortAscend, vector(TABLE_FORMAT[col][$key]));
break;
}
return null;
}
protected vague tableHandler (native comp, symbol event) {
switch (event) {
GUI Programming
91
case $select:
gui.maintain(panel);
break;
case $drill:
dump();
break;
case $hitCell:
break;
}
return null;
}
protected void dump () {
int row @= table.hitCell[0];
int col @= table.hitCell[1];
sys.println(tableModel(table, $get, row, col));
}
protected boolean canDump () {
return table.select != null;
}
}
Here is what we’ve done. We’ve set the editable and styled properties of the table to
true, and set its model property to tableModel(). Additionally, we’ve extended the
TABLE_FORMAT vector to nominate an editor for the last three columns. This enables the
user to directly edit the cells in these columns. A key point to note is how we’ve used
map() to specify the format of these columns, because each is referring to a non-literal
(e.g., dateField).
The interesting stuff happens in tableModel(), which accepts these possible commands:

$rows requires the number of table rows to be returned.

$cols requires the number table columns to be returned.

$get requires the value at the cell denoted by row and col to be returned.

$put is required for editable tables and should make the change to a cell permanent.
The updated value can be accessed using the put property of the table.

$style should return null or a vector of values that specify the font style and/or color
of the text in a cell. This has no effect unless the styled property of the table is also
set to true.

$icon should return null or the icon to be displayed in a cell. This has no effect unless
the $icon key of the column format is also set to true.

$sortAscend and $sortDescend should sort the underlying data for the model.
A minimal data model must implement the $rows, $cols, and $get commands.
92
JavaGram Agile Development
Finally, the behavior of an editable table is noticeably different from a read-only table. In
the latter, clicking on a row causes the entire row to be selected, whereas in the former,
the specific clicked cell is selected. Consequently, the dump() method is revised to behave
differently – it writes to standard output the value of a cell, not the entire row!
The visual result of the revised table is shown below.
4.4.2 Lists
A list is a table that has a single column and no heading. Functionally, however, lists are
closer to combo boxes than to tables. Lists are rarely used because combos are just as
good and take less real estate. The only situation in which they’re preferred is when you
want to select multiple values from the list, which is not possible with combos.
There is also a list element that provides a checkbox for each row. This is suitable for
situations where the user needs to nominate a number of things from a potentially large
set.
Here is a class that demonstrates the use of lists.
<jag domain="doc/code/chap4">
singleton class ListTest {
static final vector<string> CAPITAL_CITIES = [
"Sydney", "Melbourne", "Brisbane", "Perth",
"Adelaide", "Canberra", "Hobart", "Darwin"
];
static final vector<list> ACTIVITIES = [
("Recreation and Sports", true)
,("Sight Seeing", false)
,("Bush Walking", false)
,("Wine Tasting", true)
,("Dining", true)
,("Music and Theatre", true)
GUI Programming
93
];
static final vector<list> LANGUAGES = [
("English", true)
,("Italian", true)
,("Greek", true)
,("Chinese", true)
,("Vietnamese", false)
,("Japanese", false)
];
<Panel panel>
<Layout.horizontal/>
<Panel title="Australian Capital Cities">
<Layout.border/>
<Pane.scroll lay=$center>
<List cities data={CAPITAL_CITIES} multiSelect=true/>
</Pane.scroll>
<Panel lay=$south>
<Button title="Select All" action={cities.selectAll = true}/>
<Button title="Dump" action={sys.println(cities.select)}/>
</Panel>
</Panel>
<Panel title="Activities">
<Layout.border/>
<Pane.scroll lay=$center>
<List.tick acts data={ACTIVITIES}/>
</Pane.scroll>
<Panel lay=$south>
<Button title="Tick All" action={tickAll(acts, true)}/>
<Button title="Untick All" action={tickAll(acts, false)}/>
<Button title="Dump" action={sys.println(ACTIVITIES)}/>
</Panel>
</Panel>
<Panel title="Languages">
<Layout.border/>
<Pane.scroll lay=$center>
<List.tick langs model=langModel/>
</Pane.scroll>
<Panel lay=$south>
<Button title="Tick All" action={tickAll(langs, true)}/>
<Button title="Untick All" action={tickAll(langs, false)}/>
</Panel>
</Panel>
</Panel>
protected void tickAll (native lt, boolean tick) {
lt@<List.tick>.tickAll = tick;
lt@<List>.refresh = true;
}
protected vague langModel (native comp, symbol cmd, int idx) {
switch (cmd) {
case $count:
return sys.length(LANGUAGES);
94
JavaGram Agile Development
case $get:
return LANGUAGES[idx][0];
case $tick:
return LANGUAGES[idx][1] == true;
case $toggle:
return LANGUAGES[idx][1] = !LANGUAGES[idx][1]@boolean;
}
return null;
}
}
</jag>
The class uses a horizontal layout to display three lists side by side. The first list is
defined using the <List> element and displays the Australian capital cities. The second
list is created using the <List.tick> element and displays a list of tourist activities. Both
these lists are defined using direct data. Note that the expected data format for a tick-list
is a vector of lists, where each list consists of a string and a boolean. The latter controls
the tick state of the item (true means ticked).
The last list demonstrates the use of a data model, implemented by the method
langModel().
The visual result of this class is shown below.
4.5 Grids
One limitation of tables is that all rows need to conform to the same structure (i.e., share
the same columns). Some user interface scenarios are better served by allowing different
rows to be structured differently. Grids offer this flexibility, as well as the ability to
specify dependencies between the cells. Conversely, grids lack some of the strengths of
tables – you can’t use a data model because there is no uniform row structure and,
GUI Programming
95
consequently, it would be impractical to use grids for very large data sets. The choice,
therefore, involves tradeoffs.
As an example, the following class uses a grid to implement an online shopping cart. A
grid is defined using the <Grid> element, within which you can define <Column> and <Row>
elements. The purpose of a <Column> element is to specify the properties of a column,
whereas a <Row> element defines a visible row in terms of <Cell> elements that define the
data to be displayed inside the cells. Usually, the same property can be defined at cell,
column, or row level. If a property is not explicitly defined for a cell, then it’s inherited
from the column, the row, or a default cell (in that order).
<jag domain="doc/code/chap4">
singleton class CartGrid {
static final string HEAD_BG = "0xCCCCCC";
static final string HEAD_FG = "0x990000";
<Font bold style=$bold/>
<Grid grid>
<Column description size=200 kind=$text lock=true/>
<Column quantity size=50 format="0,000" align=$east kind=$number/>
<Column weight size=50 format="0,000.00kg" align=$east kind=$number lock=true/>
<Column price size=70 format="$0,000.00" align=$east kind=$number lock=true/>
<Column delete size=50 align=$center lock=true/>
<Row lock=true bgColor={HEAD_BG} fgColor={HEAD_FG}>
<Cell value="Shopping Cart" align=$center colSpan=4 font={bold}/>
</Row>
<Row lock=true bgColor={HEAD_BG} fgColor={HEAD_FG}>
<Cell value="Items in Cart" />
<Cell value="Quantity"/>
<Cell value="Weight"/>
<Cell value="Price"/>
</Row>
<Row lock=true>
<Cell value="Add Item" border=0 link={Catalog.singleton.chooseProducts()}/>
<Cell value="Shipping:" border=0/>
<Cell value=0/>
<Cell calc={shippingCost()}/>
</Row>
<Row lock=true>
<Cell value="Checkout" border=0 link={checkOut()}/>
<Cell value="Total:" border=0/>
<Cell calc={totalColumn(2)}/>
<Cell calc={totalColumn(3)}/>
</Row>
</Grid>
public void addItems (vector<Product> prods) {
for (Product prod in prods) {
int idx = grid.rows - 2;
GridRow row = new GridRow(this, prod);
grid += list(row.row, idx);
}
96
JavaGram Agile Development
grid.recalc = true;
}
void deleteRow () {
int row = grid@<Grid>.hitCell[0];
grid -= row;
grid.recalc = true;
}
protected real totalColumn (int col) {
real total = 0.0;
for (int row = 2, n = grid.rows - 1; row < n; ++row) {
vague val = gui.getCell(grid, row, col)@<Cell>.value;
if (val instanceof real)
total += val@real;
}
return total;
}
protected real shippingCost () {
return totalColumn(2) * 5.5;
}
protected void checkOut () {
//gui.toHtml(grid);
}
}
</jag>
The properties used in this example are:

size specifies the width of a column or cell.

format specifies the format for displaying values within a cell.

align specifies how a cell’s value is to be aligned.

kind specifies the kind of value to be displayed in a cell, and must be one of: $text,
$textArea, $combo, $number, $date, $time, or $tick.

lock allows a cell to be locked so that its value can’t be changed.

fgColor and bgColor control, respectively, the foreground and background color of a
cell.

value denotes the actual value displayed in a cell.

rowSpan and colSpan control the vertical and horizontal span of a cell (both default to
1).

font specifies the preferred font for displaying a cell’s value.

border specifies the thickness of a cell’s border line.

link specifies the action for a ‘hot’ cell (the cell value is underlined and when clicked
causes the link action to execute).
GUI Programming
97

calc specifies a formula for automatically calculating the value of a cell.
Here is what this grid looks like on the screen.
Note how the Add Item and Checkout cells are linked to the addItem() and checkout()
methods. Also note how the totalColumn() method is used by the calc property of the last
two cells. This method uses gui.getCell() to access the value of the cells above it, and
adds them up to dynamically update the totals. Certain events (e.g., the user editing a
cell’s value) cause JavaGram to automatically process all calc properties in a grid. This
can also be done programmatically by setting the grid’s recalc property to true, as done
by the addItem() method. JavaGram performs a recalculation of a grid by processing the
cells left to right and top to bottom. Therefore, calc properties may contain backward but
not forward cell references.
The intended behavior for the Add Item link is to display another window, listing the
available products for purchase, from which the user can make selections. These
selections are then added as rows above the Add Item row.
CartGrid.addItem() uses three other classes. Product is a simple class for representing
purchasable products.
class Product {
protected getable string id;
protected getable string name;
protected getable real weight;
protected getable real price;
public string format () {
return $"{id} {name} @${price}";
}
}
GridRow is a simple class for adding a new grid row that represents a product added to the
cart.
98
JavaGram Agile Development
class GridRow {
static final string INPUT_BG = "0xFFFFFF";
CartGrid cart;
Product product;
delayed <Row row>
<Cell value={product.format()}/>
<Cell quant value=1 bgColor={INPUT_BG}/>
<Cell calc={quant.value@int * product.getWeight()}/>
<Cell calc={quant.value@int * product.getPrice()}/>
<Cell value="Delete" link={cart.deleteRow()}/>
</Row>
public GridRow (CartGrid cart, Product product) {
this.cart = cart;
this.product = product;
}
public native getRow() {
return row;
}
}
The constructor records the grid to which the row is to be added and the product that it
represents. Note how the row is defined as a delayed class member. The reason is that
some of the row’s cell properties refer to class fields, such as cart and product. It’s
therefore vital that the row is initialized after the constructor has set these fields to valid
values.
The third and fourth cell use calc properties to, respectively, calculate the product weight
and price, based on the purchase quantity. The last cell has a link property that allows the
row to be deleted.
Finally, the Catalog class displays a window that lists the available products for the user
to choose from. The products are displayed in a table, for which we’ve setup some
dummy data. Note how the multiSelect property of table is set to true to allow the user
to select multiple rows (by holding the control or shift key down while clicking).
This window is created using a <Dialog> element (dialogs are described in the next
section).
singleton class Catalog {
vector<Product> selected;
<Dialog dialog title="Choose a Product" parent={CartGrid.singleton.grid}
width=300 height=200>
<Layout.border/>
<Pane.scroll lay=$center>
<Table table data={PRODUCTS} format={FORMAT} autoSize=false
multiSelect=true event=tableHandler/>
</Pane.scroll>
<Panel lay=$south>
GUI Programming
99
<Button title="OK" action={ok()} enable={canOk()}/>
<Button title="Cancel" action={cancel()}/>
</Panel>
</Dialog>
protected vague tableHandler (native comp, symbol event) {
switch (event) {
case $select:
gui.maintain(dialog);
break;
case $drill:
ok();
break;
}
return null;
}
protected void cancel () {
selected = null;
dialog.show = false;
}
protected void ok () {
selected @= vector();
for (int idx in table.select@vector<int>)
sys.append(selected, PRODUCTS[idx]);
dialog.show = false;
CartGrid.singleton.addItems(selected);
}
protected boolean canOk () {
return table.select != null;
}
public vector<Product> chooseProducts () {
table.select = null;
gui.maintain(dialog);
dialog.show = true;
return selected;
}
static vector<map> FORMAT = [
[$key=>$id, $title=>"ID", $width=>50, $align=>$west]
,[$key=>$name, $title=>"Name", $width=>150, $align=>$west]
,[$key=>$weight, $title=>"Weight", $width=>60, $align=>$east, $format=>"0.00kg"]
,[$key=>$price, $title=>"Price", $width=>80, $align=>$east, $format=>"$0,000.00"]
];
static vector<Product> PRODUCTS = [
[@Product id=>"EG001", name=>"Portable CD Player", weight=>0.12, price=>120.95]
,[@Product id=>"EG002", name=>"Plasma TV", weight=>28.45, price=>2199.0]
,[@Product id=>"EG003", name=>"Coffee Maker", weight=>6.8, price=>175.99]
];
}
The chooseProducts() method is the main entry point, which displays the dialog and once
the user presses the OK button, returns a vector of products that the user has selected.
100
JavaGram Agile Development
We’ve defined the enable property of the OK button such that the button is enabled only
when the user has selected at least one row from the table.
Here is what the dialog looks like.
Referring back to the CartGrid.addItem() method, it calls chooseProducts() and iterates
through the returned vector, adding a grid row for each product. A row is added by
creating an instance of GridRow and then adding the row to the grid using the notation
grid += list(row.row, idx);
The right side of this assignment is a list of two elements: a grid row and a zero-based
row index. This causes the row to be added to the grid at the nominated row position.
Choosing the first two products from the list and setting the quantity of the first row to 2
produces the following.
Now, have a look at the method CartGrid.deleteRow(). This method is invoked when the
user clicks the Delete hot link of the last cell of a product. It removes the row using the
following notation:
grid -= grid@<Grid>.hitCell[0];
GUI Programming
101
hitCell is a grid property that denotes the position of the last clicked cell. It’s a list of the
form $(row, col). We’ve used it here to determine the row of the clicked cell.
If you experiment with this example, you’ll discover that a grid’s border lines can be
dragged using the mouse to adjust the row heights and column widths. This can be
disabled by setting the fixedWidth and fixedHeight properties of the grid to true.
You can define a grid event handler in the same way as we did earlier for tables.
However, there was no need for one in this example.
You can also define an edit handler for a grid by setting its edit property to the name of a
method in the same class. This method must have the following signature.
vague editHandler (native grid, int row, int col, vague oldVal, vague newVal)
The edit handler is invoked when the user changes the value of a cell. The last four
parameters identify the cell and its old and new value. This method should either return
the final desired value for the cell, or null (if the change is to be rejected due to, for
example, it failing validation requirements). Edit handlers are usually used for complex
validation purposes, beyond what can be achieved by cell properties such as min and max.
4.6 Component Status
Most GUI components can assume different states, such as editable/read-only,
enabled/disabled, visible/invisible. By default, a component is editable, enabled, and
visible. To make a text component, numeric component, or combo read-only, set its
readOnly property to true. The enable and visible properties of a component can be
defined as boolean expressions. These expressions are evaluated by JavaGram at specific
points in time to determine if a component should be enabled/visible. A disabled
component is dimmed. An invisible component is completely hidden as if it doesn’t exist.
Here is a sample class that illustrates these properties:
<jag domain="doc/code/chap4">
singleton class Status {
static <Field.text nameEditor/>
static vector<string> STATES = ["ACT", "NT", "NSW", "QLD", "SA", "TAS", "VIC", "WA"];
static final vector<map> TABLE_FORMAT @= vector(
[$key=>$firstName, $title=>"First Name", $width=>80, $align=>$west]
,map($key=>$lastName, $title=>"Last Name", $width=>100, $align=>$west,
$editor=>nameEditor)
,[$key=>$sex, $title=>"Sex", $width=>40, $align=>$west]
);
static vector<map> persons = [
[$firstName=>"John", $lastName=>"Smith", $sex=>"Male"],
[$firstName=>"Linda", $lastName=>"Forbes", $sex=>"Female"],
[$firstName=>"Bob", $lastName=>"Smart", $sex=>"Male"]
];
102
JavaGram Agile Development
static final string HEAD_BG = "0xCCCCCC";
static final string HEAD_FG = "0x990000";
<Pane.tabbed tabs align=$west>
<Tab title="Status (1)" image={sys.use("lib/gifs/Status.gif")}>
<Panel>
<Layout.gridBag/>
<Lay row=0 col=0 weight=0.0 fill=$horizontal margin=2>
<Label title="Enabled" image={sys.use("lib/gifs/Info.gif")}/>
</Lay>
<Lay row=0 col=1 weight=0.0 fill=$horizontal margin=2>
<Label title="ReadOnly" image={sys.use("lib/gifs/Info.gif")}/>
</Lay>
<Lay row=0 col=2 weight=0.0 fill=$horizontal margin=2>
<Label title="Disabled" image={sys.use("lib/gifs/Info.gif")}
enable=false/>
</Lay>
<Lay row=1 col=0 weight=0.0 fill=$horizontal margin=2>
<Field.text value="Sample"/>
</Lay>
<Lay row=1 col=1 weight=0.0 fill=$horizontal margin=2>
<Field.text value="Sample" readOnly=true/>
</Lay>
<Lay row=1 col=2 weight=0.0 fill=$horizontal margin=2>
<Field.text value="Sample" enable=false/>
</Lay>
<Lay row=2 col=0 weight=0.0 fill=$horizontal margin=2>
<Field.date value={sys.date()}/>
</Lay>
<Lay row=2 col=1 weight=0.0 fill=$horizontal margin=2>
<Field.date value={sys.date()} readOnly=true/>
</Lay>
<Lay row=2 col=2 weight=0.0 fill=$horizontal margin=2>
<Field.date value={sys.date()} enable=false/>
</Lay>
<Lay row=3
<Combo
</Lay>
<Lay row=3
<Combo
</Lay>
<Lay row=3
<Combo
</Lay>
col=0 weight=0.0 fill=$horizontal margin=2>
data={STATES}/>
col=1 weight=0.0 fill=$horizontal margin=2>
data={STATES} readOnly=true/>
col=2 weight=0.0 fill=$horizontal margin=2>
data={STATES} enable=false/>
<Lay row=4 col=0 weight=0.0 fill=$horizontal margin=2>
<Option.tick title="Smoker" value=true/>
</Lay>
GUI Programming
103
<Lay row=4 col=2 weight=0.0 fill=$horizontal margin=2>
<Option.tick title="Smoker" value=true enable=false/>
</Lay>
<Lay row=5 col=0 weight=0.0 fill=$horizontal margin=2>
<Option.radio title="Smoker" value=true/>
</Lay>
<Lay row=5 col=2 weight=0.0 fill=$horizontal margin=2>
<Option.radio title="Smoker" value=true enable=false/>
</Lay>
<Lay row=6 col=0 weight=0.0 fill=$horizontal margin=2>
<Button title="Button" image={sys.use("lib/gifs/Person.gif")}/>
</Lay>
<Lay row=6 col=2 weight=0.0 fill=$horizontal margin=2>
<Button title="Button" image={sys.use("lib/gifs/Person.gif")}
enable=false/>
</Lay>
<Lay row=7 col=0 weight=0.0 fill=$horizontal margin=2>
<Button.toggle title="Toggle" image={sys.use("lib/gifs/Person.gif")}/>
</Lay>
<Lay row=7 col=2 weight=0.0 fill=$horizontal margin=2>
<Button.toggle title="Toggle" image={sys.use("lib/gifs/Person.gif")}
enable=false/>
</Lay>
<Lay row=8 col=0 weight=0.0 fill=$horizontal margin=2>
<List data=["Male", "Female"]/>
</Lay>
<Lay row=8 col=2 weight=0.0 fill=$horizontal margin=2>
<List data=["Male", "Female"] enable=false/>
</Lay>
<Lay row=9 col=0 weight=0.0 fill=$horizontal margin=2>
<Area.text value="Sample text\nNext line"/>
</Lay>
<Lay row=9 col=1 weight=0.0 fill=$horizontal margin=2>
<Area.text value="Sample text\nNext line" readOnly=true/>
</Lay>
<Lay row=9 col=2 weight=0.0 fill=$horizontal margin=2>
<Area.text value="Sample text\nNext line" enable=false/>
</Lay>
</Panel>
</Tab>
<Tab title="Status (2)" image={sys.use("lib/gifs/Status.gif")}>
<Panel>
<Layout.gridBag/>
<Lay row=0 col=0 weight=0.0 fill=$horizontal margin=2>
<Label title="Enabled" image={sys.use("lib/gifs/Info.gif")}/>
</Lay>
104
JavaGram Agile Development
<Lay row=0 col=1 weight=0.0 fill=$horizontal margin=2>
<Label title="Disabled" image={sys.use("lib/gifs/Info.gif")}
enable=false/>
</Lay>
<Lay row=1 col=0 weight=0.0 fill=$horizontal margin=2>
<Slider min=0 max=10 value=5/>
</Lay>
<Lay row=1 col=1 weight=0.0 fill=$horizontal margin=2>
<Slider min=0 max=10 value=5 enable=false/>
</Lay>
<Lay row=2 col=0
<ProgressBar
</Lay>
<Lay row=2 col=1
<ProgressBar
</Lay>
weight=0.0 fill=$horizontal margin=2>
title="Progress" min=0 max=10 value=5/>
weight=0.0 fill=$horizontal margin=2>
title="Progress" min=0 max=10 value=5 enable=false/>
<Lay row=3 col=0 weight=0.0 fill=$horizontal margin=2>
<Panel title="Panel">
<Label title="Name:"/>
<Field.text value="Name"/>
</Panel>
</Lay>
<Lay row=3 col=1 weight=0.0 fill=$horizontal margin=2>
<Panel title="Panel" enable=false>
<Label title="Name:"/>
<Field.text value="Name"/>
</Panel>
</Lay>
<Lay row=4 col=0 weight=0.0 fill=$both margin=2>
<Tree>
<Node title="One" image={sys.use("lib/gifs/Question.gif")}
expand=true>
<Node title="Two" image={sys.use("lib/gifs/Question.gif")}/>
</Node>
</Tree>
</Lay>
<Lay row=4 col=1 weight=0.0 fill=$both margin=2>
<Tree enable=false>
<Node title="One" image={sys.use("lib/gifs/Question.gif")}
expand=true>
<Node title="Two" image={sys.use("lib/gifs/Question.gif")}/>
</Node>
</Tree>
</Lay>
<Lay row=5 col=0 weight=0.0 fill=$both margin=2>
<Table format={TABLE_FORMAT} data={persons} editable=true/>
GUI Programming
105
</Lay>
<Lay row=5 col=1 weight=0.0 fill=$both margin=2>
<Table format={TABLE_FORMAT} data={persons} editable=true
enable=false/>
</Lay>
<Lay row=6 col=0 weight=0.0 fill=$horizontal margin=2>
<Grid>
<Column description1 size=100 kind=$text lock=true/>
<Column quantity1 size=50 format="0,000" align=$east kind=$number/>
<Row lock=true bgColor={HEAD_BG} fgColor={HEAD_FG}>
<Cell value="Items in Cart" />
<Cell value="Quantity"/>
</Row>
<Row>
<Cell value="Television" />
<Cell value=10/>
</Row>
</Grid>
</Lay>
<Lay row=6 col=1 weight=0.0 fill=$horizontal margin=2>
<Grid enable=false>
<Column description2 size=100 kind=$text lock=true/>
<Column quantity2 size=50 format="0,000" align=$east kind=$number/>
<Row lock=true bgColor={HEAD_BG} fgColor={HEAD_FG}>
<Cell value="Items in Cart" />
<Cell value="Quantity"/>
</Row>
<Row>
<Cell value="Television" />
<Cell value=10/>
</Row>
</Grid>
</Lay>
</Panel>
</Tab>
<Tab title="Disabled" image={sys.use("lib/gifs/Status.gif")} enable=false>
<Panel/>
</Tab>
</Pane.tabbed>
}
</jag>
The first two sub-tabs of Status tab illustrate the appearance of normal, read-only, and
disabled components. The third sub-tab illustrates the appearance of a disabled tab.
106
JavaGram Agile Development
4.7 Dialogs and Alerts
Dialogs and alerts are windows that are displayed as a result of the user performing an
action that requires further information to be collected, or something brought to the
attention of the user. Alerts are in fact very simple, predefined dialogs that give the user
very limited options, such as a ‘yes’ or ‘no’ response to a question.
4.7.1 Dialogs
A dialog is a window that can be displayed or hidden at will during user interaction with
an application. The content of a dialog is defined by the programmer, much in the same
way as a frame.
GUI Programming
107
Dialogs are defined using the <Dialog> element. We saw an example of this in the
Catalog class defined in the previous section. Here is another example that defines a
dialog for specifying user preferences.
<jag domain="doc/code/chap4">
<load>
"lib/gui/GuiUtil"
</load>
singleton class Dialogs {
<Dialog dial title="Preferences" parent={GuiUtil.getMainFrame()}
width=200 height=130>
<Layout.border/>
<Panel prefs>
<Layout.vertical/>
<Option.tick key=$save title="Save changes before existing"/>
<Option.tick key=$duplicates title="Allow duplicates"/>
<Option.tick key=$validate title="Validate transactions"/>
</Panel>
<Panel lay=$south>
<Button title="OK" action={ok()}/>
<Button title="Cancel" action={dial.show = false}/>
</Panel>
</Dialog>
}
</jag>
It displays the following dialog.
Like a frame, a dialog can take properties such as title, width, and height. It’s also very
common for a dialog to have one or more buttons. Once the user has finished their
interaction with the dialog, they press one of these buttons to dismiss the dialog.
A dialog can assume one of two display modes: modal or modeless. Once displayed, a
modal dialog limits the user’s interaction to that dialog, making the rest of the application
inaccessible, until the dialog is dismissed. A modeless dialog, on the other had, leaves the
user free to move back and forth between the dialog and the rest of the application. The
mode of a dialog is controlled by the modal property, which can be set to true or false
(defaults to true).
Two other useful dialog properties are parent and center. The former nominates the
dialog’s parent. This can be either a frame or another dialog (defaults to null, meaning
108
JavaGram Agile Development
that the dialog has no parent). It’s generally a good idea to always give a modal dialog a
valid parent, so that when the application is brought to the front, a visible modal dialog
pops to the front of its parent. The center property is usually also set to the parent,
causing the dialog to be centered relative to the parent.
For convenience, a dialog can be treated as a panel, so you don’t need to define a toplevel panel for it. As illustrated by the above example, we’ve defined a border layout for
this implicit panel.
4.7.2 Alerts
Occasionally, an application needs to alert the user about something, such as displaying a
warning. For example, if the user attempts to save a file and this operation fails for some
reason (such as the file being read-only) then the user should be alerted about this failure.
Here is a simple alert that warns the user about a save operation failing.
<Alert failed title="Failed" message="Can't save read-only file!"
kind=$warning owner={GuiUtil.getMainFrame()}/>
The kind property may be set to one of:

$error (for displaying an error message)

$info (for displaying feedback information)

$warning (for displaying a warning message)

$question (for asking a question)

$plain (for any other purpose)
The icon displayed inside the alert is dependent on the value of the kind property.
The alert is displayed by referring to its show property. This is a read-only value, which
returns the zero-based index of the button the user presses to dismiss the dialog (or null
for an alert with just one button).
The above alert produces the following window.
Here is another alert that asks a question.
GUI Programming
109
<Alert ask title="Save?" message="Do you want to save your changes?" kind=$question
options=["Yes", "No", "Cancel"] enter=0 owner={GuiUtil.getMainFrame()}/>
The options property may be set to a vector of up to three strings, which specify the titles
of the buttons to be displayed in the alert. The show property returns the zero-based index
of the button pressed by the user. The enter property may be set to the index of the
default button, so that the user can dismiss the alert by simply pressing the enter key.
The above alert displays the following window.
Here, for example, if the user presses the No button ask.show returns 1.
The Yes/No/Cancel alert is so commonly used that there is a special element for it, so that
you don’t need to specify the buttons. The following is equivalent to the above alert.
<Alert.ync ask title="Save?" message="Do you want to save your changes?"
enter=0 owner={GuiUtil.getMainFrame()}/>
The <Alert> element is useful when you’re likely to reuse the alert. For more ad hoc
purposes, it’s easier to use the sys.alert() method. For example,
gui.alert("Save?", "Do you want to save your changes?",
GuiUtil.getMainFrame(), $question)
is equivalent to the above alert, but returns a symbol that denotes the pressed button (i.e.,
one of: $ok, $yes, $no, $cancel). However, gui.alert() is more limited than <Alert> in
that, for a question alert, you can’t customize the buttons.
4.7.3 File Chooser
Occasionally, an application may need to ask the user to nominate one or more files or a
directory for the task at hand. For example, if the user selects a command to save
something to a file, the application may ask the user to nominate a directory for the file to
be saved to.
JavaGram provides a predefined dialog for this purpose which can be accessed using the
<FileChooser> element. Here is an example:
<FileChooser fc owner={GuiUtil.getMainFrame()} kind=$open
title="Choose file(s)" path="C:/JavaGram/" ext=["doc","zip"]
110
JavaGram Agile Development
multiSelect=true desc="DOC and ZIP files" label="Do it"/>
The kind property may be set to $open (for selecting files for opening) or $save (for
selecting files for saving). The dialog doesn’t actually open or save files, but rather this
property denotes the intent. The ext property can be set to a vector of permissible file
extensions. The multiSelect property controls whether selection should be limited to one
or multiple items.
As with alerts, the dialog is displayed by accessing its show property, which subsequently
returns the selection result, which may be a path (if multiSelect is false), a vector of
paths (if multiSelect is true), or null (if nothing is selected).
The above example shows the following dialog.
4.7.4 Color Chooser
To allow the user to choose a color in an application (e.g., the background color of a
report to be generated), use the <ColorChooser> element.
Here is an example:
<ColorChooser cc owner={GuiUtil.getMainFrame()}
title="Choose a color" color="0xAABBFF"/>
The color property may be used to nominate the default color, in which case, this color
becomes the initial selection when the dialog is displayed.
As before, the dialog is displayed by accessing its show property, which subsequently
returns the selected color as an integer value, or null if nothing is selected.
GUI Programming
111
4.8 Graphs
Graphs allow you to visualize numeric data, and are especially useful when you want to
present a summary of a potentially large data set to the user.
4.8.1 Pie Charts
The simplest type of graph is a pie chart, where a set of numbers (a single series) is
presented as a circle divided into ‘pie slice’ segments. Each segment’s size is
proportional to the number it represents.
A pie chart is defined using the <Graph.pie> element. The graph data can be specified
either directly or through a data model. Here is an example that illustrates both styles.
<jag domain="doc/code/chap4">
singleton class Graphs {
protected vector<map> pieData = [
[$value=>25, $title=>"Software", $color=>"0x77FF77"],
[$value=>15, $title=>"Hardware", $color=>"0x7777FF"],
[$value=>35, $title=>"Maintenance", $color=>"0xFF7777"]
];
<Pane.tabbed pane align=$south>
<Tab title="Pie direct">
<Panel>
<Layout.border/>
<Graph.pie legend=true legendLay=$south format="0"
data={pieData} event=handler/>
</Panel>
</Tab>
<Tab title="Pie model">
<Panel>
<Layout.border/>
<Graph.pie legend=true legendLay=$east format="0"
112
JavaGram Agile Development
model=pieModel event=handler/>
</Panel>
</Tab>
</Pane.tabbed>
protected vague pieModel (native pie, symbol cmd, int idx) {
switch (cmd) {
case $count: return sys.length(pieData);
case $value: return pieData[idx][$value];
case $title: return pieData[idx][$title];
case $color: return pieData[idx][$color];
}
return null;
}
protected void handler (native comp, symbol event) {
sys.println(gui.tag(comp), " event: ", event);
}
}
</jag>
Here is what the pie graph looks like.
When using the direct data approach, the data must be a vector of maps, where each map
has three keys:

$value (the data value).

$title (the title of the pie segment).

$color (the color of the pie segment).
GUI Programming
113
The data model approach specifies the data model as a method in the same class that
accepts the above keys as commands, as well as a $count command that returns the
number of data values.
A pie chart may optionally have a legend. To display the legend, set the legend property
to true. The legend may be positioned south or east of the pie chart. This is controlled by
the legendLay property. You can also specify a format property to control the way the
segment values are formatted.
To detect user events, you can specify an event handler. The one we’ve specified in this
example, simply prints out the graph type and the event. We’ll reuse this handler for other
graphs defined in this section.
Another useful pie chart property is drillable. When set to true, the pie segment under
the mouse pointer is highlighted and when the user clicks on the pie segment, a $drill
event is generated. The program can respond to this by, for example, displaying detailed
information for the pie segment.
4.8.2 Line Graphs
A line graph is useful for displaying one or more series of data on a two-dimensional
plane, where each series consists of a sequence of samples. The graph has two axes: the
x-axis represents the sample reference points, and the y-axis the sample values. For
example, the x-axis may represent dates and the y-axis, a city’s average temperature for
each day. If this data is collected for three cities, then we’ll have three series – one for
each city.
A line graph is defined using the <Graph.line> element. As before, the graph data can be
specified either directly or through a data model. Here is an extension of the earlier
Graphs class that uses the direct data approach.
singleton class Graphs {
//...
protected vector<vector> graphData = [
[10, 20, 13.2, 100, 87, 52.7, 11, 28, 50],
[89, 2, 872, 883, 22, 91, 263, 228, 127, 399, 222]
];
protected vector<map> graphStyles = [
[$title=>"Age", $color=>"0x77FF77", $dash=>0, $weight=>1, $marker=>$triangle,
$fill=>true, $label=>true],
[$title=>"Income", $color=>"0x7777FF", $dash=>4, $weight=>1, $marker=>$diamond,
$fill=>false, $east=>true, $type=>$line]
];
<Pane.tabbed pane align=$south>
//...
<Tab title="Graph direct">
<Panel>
114
JavaGram Agile Development
<Layout.border/>
<Graph.line bgColor="0xFFFFFF" legend=true legendLay=$south
editable=true xFormat="0.0" style={graphStyles}
data={graphData} event=handler xTitle="Age" yTitle="Icome"
xTitle="Age" yTitle="Icome"/>
</Panel>
</Tab>
</Pane.tabbed>
//...
}
A line graph’s direct data is a vector of the data for each series, which is itself a vector of
values. Style information is specified using the style property, which must be a vector of
maps. Each map specifies the style of its corresponding series.
The appearance of our sample line graph is shown below.
You can make a line graph editable by setting its editable property to true. As a result,
each of the sample points can be dragged by the mouse up or down to change its y-value.
The x-values cannot be directly changed.
4.8.3 Bar and Stack Graphs
A bar graph is essentially the same as a line graph, except that each y-value is displayed
as a vertical bar. All other graph properties are as before. Here is an example, which is
equivalent to the line graph described earlier, but is specified using the <Graph.bar>
element and uses a data model instead of direct data.
GUI Programming
115
<Tab title="Bar & Line Graph">
<Panel>
<Layout.border/>
<Graph.bar bgColor="0xFFFFFF" legend=true legendLay=$east editable=true
xFormat="0.0" style={graphStyles} bands={graphBands}
model=graphModel event=handler
xTitle="Age" yTitle="Icome"/>
</Panel>
</Tab>
The data model is defined as:
protected vague graphModel (native pie, symbol cmd, int series, int idx, real val) {
switch (cmd) {
case $series: return sys.length(graphData);
case $samples: return sys.length(graphData[series]);
case $key: return idx;
case $value: return graphData[series][idx];
case $putNext: graphData[series][idx] = val; break;
}
return null;
}
The $putNext command in the data model is needed only for an editable graph. It’s called
when the user drags a bar’s top edge up or down (or a line graph’s control point up or
down). Two other related commands are $putable (which can be used to decide whether a
graph is editable) and $putLast (which is invoked when the user completes an edit by
releasing the mouse button).
The resulting bar chart is shown below.
116
JavaGram Agile Development
There is also a variant of the bar graph (defined using the <Graph.stack> element) that
stacks the bars for each x-axis value on top of each other.
Graph elements accept many more properties (see Chapter 11 for details). Some of these
properties can be set by right clicking on a graph and selecting from the resulting popup
menu.
4.8.4 Bubble Graph
A bubble graph is a variant of line graph where the data points are displayed as bubbles
(solid circles). Bubble graphs are not editable. Whereas (for each series) a line graph
specifies a y-value for a given x-value, a bubble graph specifies a y-value as well as a
bubble size and an optional bubble color for a given x-value. Bubble graphs are useful for
visualizing multi-faceted data. For example, a line graph may be used to show the GDP
(y-axis) of countries (series) over a 10 year period (x-axis). A bubble graph can represent
the same, but also show the average per-capita income (bubble size), and the trade
balance (bubble color) of each country. The latter can range from green (trade surplus) to
red (trade deficit). You should always use a data model (not direct data) for a bubble
graph.
protected vector<map> bubbleStyles = [
[$title=>"Age", $color=>"0x77FF77", $label=>true],
[$title=>"Income", $color=>"0x7777FF", $weight=>0, $east=>true]
];
<Tab title="Bubble Graph">
<Panel>
<Layout.border/>
GUI Programming
117
<Graph.bubble bgColor="0xFFFFFF" legend=true legendLay=$east
xFormat="0.0" style={bubbleStyles} model=graphModel
event=handler xTitle="Age" yTitle="Icome"/>
</Panel>
</Tab>
The data model is defined as (note the use of $bubble and $bubbles in the data model):
vector<map> BUBBLE_LEGEND = [
[$color=>"#FF0000", $title=>"Deficit"],
[$color=>"#00FF00", $title=>"Credit"],
[$color=>"#0000FF", $title=>"Balanced Account"]
];
protected vague graphModel (native graph, symbol cmd, int series, int idx, real val) {
switch (cmd) {
case $series: return sys.length(graphData);
case $samples: return sys.length(graphData[series]);
case $key: return idx;
case $value: return graphData[series][idx];
case $bubble: return bubbleData[series][idx];
case $bubbles: return BUBBLE_LEGEND;
case $putNext: graphData[series][idx] = val; break;
case $label: return sys.format(graphData[series][idx], "0%");
}
return null;
}
118
JavaGram Agile Development
4.8.5 Pipe Graph
A pipe graph is a graph where data is represented by horizontal pipes, and is especially
useful for displaying heat maps. Each pipe may consist of zero or more segments, where
the segments are typically drawn in different colors. A pipe graph has a single horizontal
axis (x-axis). Pipe graphs are not editable.
<Tab title="Pipe Graph">
<Panel bgColor="0xFFFFFF">
<Layout.border/>
<Pane.scroll horizontal=false border=$empty>
<Graph.pipe bgColor="0xFFFFFF" title="Sample Pipe Graph"
legend={pipeLegend} legendLay=$east
pipeTitlePos=$north grid=true minValue=0 xTitle="Weight"
xFormat="0.0%" model=pipeModel event=pipeHandler/>
</Pane.scroll>
</Panel>
</Tab>
The data model and event handler are defined as:
protected vague pipeModel (native comp, symbol cmd, int row, int segment) {
switch (cmd) {
case $count: return row == null
? sys.length(pipeData) : sys.length(pipeData[row]) - 1;
case $value: return pipeData[row][segment + 1]@map[$value];
case $color: return pipeData[row][segment + 1]@map[$color];
case $title: return segment == null
? pipeData[row][0] : pipeData[row][segment + 1]@map[$title];
}
return null;
}
protected void pipeHandler (native comp, symbol event) {
sys.println(gui.tag(comp), " event: ", event);
sys.println(comp@<Graph.pipe>.select);
}
protected vector<vector> pipeData = [
["First Bar"
, [$value=>20.5, $color=>"0xFF0000", $title=>"A1"]
, [$value=>70.5, $color=>"0x0000FF", $title=>"A2"]
, [$value=>30.5, $color=>"0x00FF00", $title=>"A3"]
]
, ["Second Bar"
, [$value=>55.2, $color=>"0xFF0000", $title=>"B1"]
, [$value=>86.1, $color=>"0x0000FF", $title=>"B2"]
]
, ["Third Bar google"
, [$value=>29, $color=>"0xFF0000", $title=>"C1"]
, [$value=>45.4, $color=>"0x0000FF", $title=>"Cg2"]
, [$value=>65, $color=>"0xFF0000", $title=>"C3"]
, [$value=>12, $color=>"0x0000FF", $title=>"C4"]
GUI Programming
119
]
];
4.8.6 Gauge and Range Bar
A gauge is a visual meter, similar to those used in a car dashboard. Use this component to
visualize a value and color-code its bands. The gauge value (denoted by its needle) can be
accessed via the value property.
A range bar is like a slider: it has a minimum and a maximum value, but its thumb
denotes two values: start and finish, both of which fall in between min and max, and
start is always less than finish. The thumb size is proportional to the ‘distance’ between
start and finish.
<Tab title="Gauge/Range">
<Panel>
<Layout.border/>
<Slider gaugeSlider lay=$north min=0 max=100 value=75
action={gaugeSliderAdjusted()}/>
<Gauge gauge lay=$center title="Sample Gauge" fgColor="0x0000DD"
bgColor="0xFFFFFF" data={gaugeData} value=25/>
<RangeBar range lay=$south align=$south pause=1000 min=0 max=5
start=1 finish=3 step=1 fgColor="0x0000DD" bgColor="0xFFFFFF"
format="0.00%" action={sys.println("dragged")}/>
</Panel>
</Tab>
protected void gaugeSliderAdjusted () {
gauge.value = gaugeSlider.value;
120
JavaGram Agile Development
gauge.refresh = true;
}
protected vector<map> gaugeData = [
[$value=>0.0, $title=>"0%", $color=>"0xFFFFFF"]
, [$value=>25.0, $title=>"25%", $color=>"0x990000"]
, [$value=>50.0, $title=>"50%", $color=>"0xFF0000"]
, [$value=>75.0, $title=>"75%", $color=>"0x0000FF"]
, [$value=>100.0, $title=>"100%", $color=>"0x00FF00"]
];
4.8.7 Flow Graphs
A flow graph is a combination of ‘places’ and directed ‘links’ that interconnect the
places. It can be used to represent concepts such as dataflow and workflow. Flow graph is
currently not available in JAG.swf.
Here is an example that depicts a workflow representation.
<Tab title="Flow Graph direct">
<Panel>
<Layout.border/>
<Panel lay=$north>
<Button.toggle boxButn title="Create Box" action={createFgPlace($box)}/>
<Button.toggle queueButn title="Create Queue" action={createFgPlace($queue)}/>
<Button title="Auto Size" action={autoSizeFg()}/>
<Button title="Delete" action={deleteFgElement()}
enable={flowGraph1.select != null}/>
</Panel>
<Graph.flow flowGraph1 lay=$center bgColor="0xDDDDDD" editable=true data={fgData}
GUI Programming
121
event=fgHandler/>
</Panel>
</Tab>
The graph data is defined as:
protected vector<map> fgData = [
[$bounds=>(14, 11, 95, 43), $color=>"0x0000FF", $desc=>"old and new pair", $shape=>$box,
$title=>"Select Specification", $type=>$place]
, [$bounds=>(117, 92, 73, 28), $color=>"0xFF0000", $shape=>$queue, $title=>"Pending Spec",
$type=>$place]
, [$bounds=>(174, 12, 126, 43), $color=>"0x0000FF", $desc=>"auto-detect differences",
$shape=>$box, $title=>"Analyze Specification", $type=>$place]
, [$bounds=>(320, 81, 80, 28), $color=>"0xFF0000", $shape=>$queue, $title=>"Analyzed Spec",
$type=>$place]
, [$bounds=>(302, 141, 116, 54), $color=>"0x0000FF",
$desc=>"update dependent\ndocuments & systems", $shape=>$box, $title=>"Apply Changes",
$type=>$place]
, [$bounds=>(169, 159, 86, 28), $color=>"0xFF0000", $shape=>$queue,
$title=>"Processed Spec", $type=>$place]
, [$bounds=>(47, 159, 78, 28), $color=>"0x0000FF", $shape=>$box, $title=>"QC Changes",
$type=>$place]
, [$bounds=>(54, 245, 61, 28), $color=>"0xFF0000", $shape=>$queue, $title=>"Failed Spec",
$type=>$place]
, [$bounds=>(248, 244, 68, 28), $color=>"0xFF0000", $shape=>$queue, $title=>"Passed Spec",
$type=>$place]
, [$color=>null, $ends=>(0, 1), $shape=>$line, $type=>$link]
, [$color=>null, $ends=>(1, 2), $shape=>$line, $type=>$link]
, [$color=>null, $ends=>(2, 3), $shape=>$line, $type=>$link]
, [$color=>null, $ends=>(3, 4), $shape=>$line, $type=>$link]
, [$color=>null, $ends=>(4, 5), $shape=>$line, $type=>$link]
, [$color=>null, $ends=>(5, 6), $shape=>$line, $type=>$link]
, [$color=>null, $ends=>(6, 7), $shape=>$line, $title=>"failed", $type=>$link]
, [$color=>null, $ends=>(6, 8), $shape=>$line, $title=>"passed", $type=>$link]
];
The various methods used by the graph definitions are as follows. See Chapter 11 for a
full description of flow graph properties and event handling.
protected void autoSizeFg () {
flowGraph1.autoSize = $all;
flowGraph1.refresh = true;
}
protected void createFgPlace (symbol shape) {
flowGraph1.click = true;
}
protected void deleteFgElement () {
int idx @= flowGraph1.select;
flowGraph1.select = null;
deleteFgElementAux(idx);
122
JavaGram Agile Development
flowGraph1.refresh = true;
}
protected void deleteFgElementAux (int idx) {
if (idx == null)
return;
map elem = fgData[idx];
if (elem[$type] == $place) {
vector<int> indices @= vector(idx);
// Find connected links:
for (int i = 0, n = sys.length(fgData); i < n; ++i) {
map elem = fgData[i];
if (elem[$type] == $link) {
list ends @= elem[$ends];
if (ends[0] == idx || ends[1] == idx)
sys.append(indices, i);
}
}
sys.sort(indices, false);
for (int i in indices) {
renumberElementsAbove(i, -1);
sys.remove(fgData, i, $at);
}
} else {
renumberElementsAbove(idx, -1);
sys.remove(fgData, idx, $at);
}
}
protected void renumberElementsAbove (int idx, int by) {
for (int i = 0, n = sys.length(fgData); i < n; ++i) {
map elem = fgData[i];
if (elem[$type] == $link) {
list ends @= elem[$ends];
if (ends[0]@int > idx)
ends[0] = ends[0]@int + by;
if (ends[1]@int > idx)
ends[1] = ends[1]@int + by;
}
}
}
protected void fgHandler (native comp, symbol event) {
list click @= comp@<Graph.flow>.click;
switch (event) {
case $create:
symbol shape = boxButn.select ? $box : $queue;
map place = map (
$type => $place,
$title => "New",
$shape => shape,
$color => shape == $queue ? "0xFF0000" : "0x0000FF",
$bounds => list(click[0]@int - 30, click[1]@int - 10, 60, 20)
);
GUI Programming
123
sys.append(fgData, place);
boxButn.select = false;
queueButn.select = false;
comp@<Graph.flow>.refresh = true;
break;
case $link:
map newLink = map (
$type => $link,
$title => "new",
$shape => $line,
$color => "0x000000",
$ends => list(click[0], click[1])
);
sys.append(fgData, newLink);
comp@<Graph.flow>.refresh = true;
break;
case $relink:
map oldLink = fgData[comp@<Graph.flow>.select@int];
list ends @= oldLink[$ends];
ends[click[0]@int == ends[0] ? 0 : 1] = click[1]@int;
comp@<Graph.flow>.refresh = true;
break;
case $select:
case $drill:
sys.println(event);
break;
}
}
The resulting flow graph is shown below.
124
JavaGram Agile Development
As before, a flow graph can be defined using a model instead of direct data. For example:
protected vague fgModel (native comp, symbol cmd, int idx, map edit) {
switch (cmd) {
case $count: return sys.length(fgData);
case $edit: fgData[idx] += edit; break;
default: return fgData[idx][cmd];
}
return null;
}
4.9 Menus and Toolbars
Most applications organize their user-accessible commands in menus, toolbars, or both.
The normal convention is for these to appear at the top of an application’s frame for easy
access.
4.9.1 Pull-down Menus
Pull-down menus are accessed via a menu bar that appears at the very top of an
application frame. The menu bar is defined using the <MenuBar> element, within which
you can define menus using the <Menu> element. A menu itself may contain <MenuItem> or
<Seperator> elements. The latter is for separating logical groups of menu items using a
horizontal bar.
Let’s add a menu bar to our demo application.
GUI Programming
125
<jag domain="doc/code/chap4">
<load>
"lib/gui/GuiUtil"
"lib/gui/Html"
"lib/io/Export"
"lib/lang/Common"
"doc/code/chap4/DemoApp"
</load>
singleton class Menus {
<MenuBar menuBar>
<Menu title="File">
<MenuItem title="Print Preview" image={sys.use("lib/gifs/DrillDown.gif")}
action={previewHtml()} accelerator="control P"/>
<Separator/>
<MenuItem title="Exit" image={GuiUtil.blankIcon} action={sys.exit()}/>
</Menu>
<Menu title="View">
<MenuItem.tick view0 title="Person" action={selectTab(0)}/>
<MenuItem.tick view1 title="Tree" action={selectTab(1)}/>
<MenuItem.tick view2 title="Table" action={selectTab(2)}/>
<MenuItem.tick view3 title="Lists" action={selectTab(3)}/>
<MenuItem.tick view4 title="Grid" action={selectTab(4)}/>
<MenuItem.tick view5 title="Dialog" action={selectTab(5)}/>
<MenuItem.tick view6 title="Graphs" action={selectTab(6)}/>
</Menu>
</MenuBar>
static final string GEN_DIR = sys.pathConc(sys.root, "generated/");
protected void selectTab (int idx) {
DemoApp.singleton.selectTab(idx);
view0.select = idx == 0;
view1.select = idx == 1;
view2.select = idx == 2;
view3.select = idx == 3;
view4.select = idx == 4;
view5.select = idx == 5;
view6.select = idx == 6;
}
protected void previewHtml () {
preview($html, "Preview.html");
}
protected void preview (symbol syntax, string fileName) {
string path = null;
stream file = null;
try {
sys.createPath(GEN_DIR);
path = sys.pathConc(GEN_DIR, fileName);
file = sys.open(path, "w");
switch (syntax) {
case $html:
sys.println(file, HtmlGenerator.htmlHeader("Demo"));
126
JavaGram Agile Development
gui.toHtml(DemoApp.singleton.frontTab(), file, GEN_DIR);
sys.println(file, HtmlGenerator.htmlTrailer());
break;
case $xml:
case $jag:
map rec = map();
gui.save(DemoApp.singleton.frontTab(), rec);
Export.singleton.toStream(rec, syntax, file);
break;
}
}
catch (Exception e) {
gui.alert("Error", e.getMessage(), GuiUtil.getMainFrame(), $error);
}
finally {
if (file != null) {
sys.close(file);
Desktop.open(path);
}
}
}
protected void help () {
gui.alert("Help", "This command not yet implemented!",
GuiUtil.getMainFrame(), $warning);
}
}
</jag>
The menu bar in this class defines two menus. The File menu has two simple commands
– one for print-previewing the current tab and one for exiting the application. Note the
similarity between a menu item and a button’s properties, such as title, image, and
action. The accelerator property allows you to define keyboard shortcuts for menu
items. The View menu demonstrates tick-able menu items. The action handler for these
items calls selectTab() which in turn brings the relevant application tab to the front and
sets the select (tick) state of all the items in this menu so that only the selected one it
ticked.
Our sample menu bar is easily added to the application frame using the <Indirect>
element.
<App app lookAndFeel=$windows>
<Frame frame title="Demo App" width=550 height=400 event=frameHandler>
<Indirect ref={Menus.singleton.menuBar}/>
//...
</Frame>
</App>
Here is the appearance of the application with the added menu bar.
GUI Programming
127
4.9.2 Popup Menus
A popup menu is a menu that remains hidden until the user presses the right mouse
button, and is useful for implementing context-sensitive commands. A popup menu is
always attached to another GUI element (its owner). The same popup menu may be
attached to multiple owners.
You define a popup menu using the <Menu.popup> element and set its owner property to
either a GUI element or a vector of GUI elements. Here is a simple example that defines
a popup menu for the demo application’s menu bar, containing two dummy commands
and a separator (this is just an illustrative example – we don’t really recommend attaching
popup menus to menu bars!)
<Menu.popup popup title="Popup Example" owner={menuBar}>
<MenuItem title="Command A"/>
<Separator/>
<MenuItem title="Command B"/>
</Menu.popup>
To access the popup menu, position the mouse pointer on the menu bar (but not on a
menu title) and hold the right mouse button down.
Because popup menus can be shared by different GUI elements, when processing a menu
command, you might want to know its source (i.e., the actual owner on which the rightclick has taken place). You can get the relevant owner using the invoker property of the
popup menu.
4.9.3 Toolbars
A toolbar is structurally very similar to a menu, except that instead of menu items, it
contains buttons. We’ll add a simple toolbar to our demo application that has just four
128
JavaGram Agile Development
buttons. The first button is equivalent to the first command in the File menu. The next
two buttons are intended to display the data binding for the current application tab using
XML or JAG syntax. All three are implemented using the preview() method, which
writes the relevant data to a file and uses the Desktop.open() library method to open and
display the file. The last button simply displays an alert box to state that it’s not yet
implemented.
<ToolBar toolBar floatable=true border=$empty>
<Button tooltip="Print Preview" image={sys.use("lib/gifs/DrillDown.gif")}
action={previewHtml()}/>
<Separator/>
<Button tooltip="Show XML Data" image={sys.use("lib/gifs/ViewXml.gif")}
action={preview($xml, "XmlData.txt")}/>
<Button tooltip="Show JAG Data" image={sys.use("lib/gifs/ViewJAG.gif")}
action={preview($jag, "JagData.txt")}/>
<Separator/>
<Button tooltip="Help" image={sys.use("lib/gifs/Help.gif")} action={help()}/>
<Separator/>
<Option.tick clock title="Show Clock"
action={DemoApp.singleton.showClock(clock.select)}/>
</ToolBar>
Adding the toolbar to our demo application is easy.
<App app lookAndFeel=$windows>
<Frame frame title="Demo App" width=550 height=400 event=frameHandler>
//...
<Panel>
<Layout.border/>
<Indirect ref={Menus.singleton.toolBar} lay=$north/>
<Pane.tabbed tabs lay=$center>
//...
</Pane>
<Panel clockPanel lay=$south visible=false>
<Field.text clock readOnly=true width=150 align=$center/>
</Panel>
</Panel>
</Frame>
</App>
Here is the appearance of the application with the added toolbar.
GUI Programming
129
4.10 Google Map
This component provides access to Google map functionality. To use Google maps, you
must obtain a map API key from Google and assign it to the key property.
The following example illustrates the use of Google map, map markers, and geocoding.
<jag domain="doc/code/chap4">
<load>
"lib/lang/Common"
</load>
singleton class Map {
<GoogleMap gmap key="..." navControl=true typeControl=true event=mapHandler/>
protected void mapHandler (native comp, symbol event) {
sys.println(event);
if (event == $ready) {
gmap.at = $(48.85, 2.34);
gmap.zoom = 13;
gmap.geocode = "Canterbury Melbourne AU";
native marker;
marker = gui.create($MapMarker, map(
$at=>$(48.86, 2.35), $event=>Util.strToId("markerHandler"),
$radius=>2000, $fgColor=>"0xFF0000", $bgColor=>"0x22FFAAAA"
), this);
gmap += marker;
marker = gui.create($MapMarker, map(
$at=>$(48.85, 2.34), $image=>sys.use("lib/gifs/House.gif"),
$event=>Util.strToId("markerHandler"), $draggable=>true
), this);
gmap += marker;
marker = gui.create($MapMarker, map(
$at=>$(48.86, 2.35), $event=>Util.strToId("markerHandler"),
130
JavaGram Agile Development
$title=>"A", $tooltip=>"My Tooltip"
), this);
gmap += marker;
} else if (event == $geocode) {
sys.println(gmap.geocode);
}
}
protected void markerHandler (native comp, symbol event) {
//sys.println(event);
switch (event) {
case $rollOver:
comp@<MapMarker>.info = "<html>Sample info window<br><b>Name:</b> John Smit
h<br><b>Occupation:</b> Civil Engineer<br>Another line</html>";
break;
case $rollOut:
comp@<MapMarker>.info = null;
break;
case $drag:
break;
}
}
}
</jag>
Specific locations on a map can be marked using the <MapMarker> component. However,
because a marker can’t be added to a map until the map is ready, it’s best to create a
marker using gui.create() after the map becomes ready. To add a marker to a map, we
use the syntax map += marker. To remove it, we use the syntax map -= marker.
To find out the geo-coordinate of a location (e.g., country, state, suburb, address), we set
geocode property to a string representation of it. This property should not be accessed
until the map is ready. The response (reported via the $geocode event) can be accessed by
getting this property, which will be a map having these keys: $request (denotes the
original request string), $error (denotes an error string when an error occurs), $response
(denotes a vector when the request is successful). Each element of the vector denoted by
$response is a list of three elements: (resolvedAddress, latitude, longitude).
The sample code displays the following map.
GUI Programming
131
4.11 Code Editor
In some situations, you might find it useful to display JavaGram code in a panel, or even
allow the user to edit such code. Imagine an advanced feature that allows the user to
customize certain application behavior by providing JavaGram classes that subclass
existing classes. A good example is allowing the user to define customized menu items
that perform user-defined tasks such as customized reporting.
JavaGram provides a GUI element for this purpose, called <Jade.editor>, which is in fact
a wrapping of the syntax highlighting editor utilized by the JavaGram IDE.
Let’s add a tab to our demo application that uses the code editor component for viewing
and editing JavaGram files.
<jag domain="doc/code/chap4">
<load>
"lib/gui/GuiUtil"
</load>
singleton class Editor {
<Panel panel>
<Layout.border/>
<Panel lay=$north>
<Button title="Open" action={fc.show}/>
<Button title="Save" action={save()} enable={canSave()}/>
</Panel>
<Jade.editor edit lay=$center validation=$semantics event=editHandler/>
</Panel>
<FileChooser fc owner={GuiUtil.getMainFrame()} kind=$open title="Open"
132
JavaGram Agile Development
ext=["jag"] desc="JavaGram files"/>
protected string path;
protected void fcHandler (vague res) {
if (res instanceof string)
edit.open = path = res @ string;
}
protected void save () {
edit.save = path;
}
protected boolean canSave () {
return path != null && edit.modified;
}
protected void editHandler (native comp, symbol event) {
if (event == $modified)
gui.maintain(panel);
}
}
</jag>
<Jade.editor> supports many properties, of which we’ve only illustrated a few here. The
validation property denotes the level of validation performed by the editor (defaults to
$syntax). We’ve set this to $semantics for the highest level of validation.
We’ve defined two buttons for opening and saving files. The former uses the file chooser
element to prompt the user to choose a file for opening. The file is opened by setting the
open property of the editor to the file path. Conversely, the file is saved by setting the
save property to the target file’s path.
We’ve also defined an event handler which reacts to a $modified event (this is generated
each time a change is made to the editor’s content) by maintaining the state of the panel.
Here is what the editor looks like after opening a file.
GUI Programming
133
4.12 Working with HTML
HTML is an important and useful notation in modern GUI programming. Its many uses
include: providing online documentation and help pages, generating reports, providing
linkage to world-wide web content, and as a GUI navigation component in its own right.
4.12.1 Displaying HTML
JavaGram provides a GUI element, called <Area.url>, for displaying HTML content.
This is somewhat similar to <Area.text> except that the content can be specified using a
url property.
Let’s add a tab to our demo application to demonstrate.
<jag domain="doc/code/chap4">
<load>
"lib/gui/GuiUtil"
</load>
singleton class HTML {
<Panel panel>
<Layout.border/>
<Panel lay=$north>
<Button title="Open File" action={fc.show}/>
<Label title="URL:"/>
<Field.text addr cols=20 value="http://"/>
<Button title="Go" action={go()}/>
</Panel>
134
JavaGram Agile Development
<Pane.scroll lay=$center>
<Area.url area readOnly=true event=anchorHandler/>
</Pane.scroll>
</Panel>
<FileChooser fc owner={GuiUtil.getMainFrame()} kind=$open title="Open"
ext=["html"] desc="HTML files" result=fcHandler/>
protected void fcHandler (vague res) {
if (res instanceof string)
area.url = "file:/" + res @ string
}
protected void go () {
string url @= addr.value;
if (url != "") {
try {
area.url = url;
}
catch (Exception e) {
gui.alert("Failed", e.getMessage(), GuiUtil.getMainFrame(), $error);
}
}
}
protected void anchorHandler (native comp, string url) {
if (sys.strChar(url, '#') != null) {
vector<string> parts = sys.strSplit(url, "#");
if (sys.length(parts) > 1)
area.scrollTo = parts[1];
} else
area.url = url;
}
}
</jag>
Although a url area can be editable, it’s often better to set the readOnly property to true so
that the user doesn’t accidentally alter the content.
Note the different between the signature of the event handler for this component,
anchorHandler(), compared to other components: the second argument is a URL string
rather than an event symbol. The handler is called when the user clicks on an anchor. If
url contains a # then the reference is a local one (i.e., in the same file), in which case we
set the scrollTo property of the component to the tag after the # character. This causes an
automatic scroll to the target of the anchor. Otherwise, url is deemed to be an external
reference, so we simply set the url property of the component to it.
We’ve provided two ways of ‘opening’ HTML content. The Open button displays a file
chooser so that the user can nominate a local HTML file. For such files, we always
prepend the file path with the "file:/" string so that it’s treated as a local file reference.
The second method consists of a text field for specifying a URL and a Go button that
attempts to open that URL. There is always a possibility that the nominated URL can’t be
GUI Programming
135
opened due to, for example, being invalid or no network connection being available. So
we’ve added some exception handling code to cater for such cases.
Here is what the new tab looks like after opening a local HTML file.
JavaGram supports a third style of anchor that consists of an arbitrary JavaGram
expression enclosed in braces. This can be used to tie anchors to programmer-defined
actions, such as jumping to a different area of the application. This type of anchor is
supported by the lib/gui/Html.jag library script. For examples of how it’s used, please
refer to the Quest sample application in Chapter 8.
4.12.2 Generating HTML Reports
Most applications have some level of report generation capability. A common
requirement is the ability to generate a report that captures what the user sees on the
screen. For example, if the user has performed a search and is viewing the search result,
they might want to produce a printable report that captures the search result. JavaGram
provides a method, gui.toHtml(), for converting the logical data displayed in a GUI
component to HTML. It has the following signature.
void gui.toHtml (native comp, stream out = null, string dir = null)
This method writes an HTML representation of comp (and the data it contains) to the
stream denoted by out, or sys.out when out is null. It ignores containers whose report
property is set to false. The dir parameter specifies the directory into which the caller
intends to place the HTML file (defaults to the current user directory) – any generated
image files are placed in this directory.
136
JavaGram Agile Development
If you add HTML header and trailer information to what this method produces then
you’ll have a complete HTML report. For an example, see the Menus.preview() method
described earlier in this chapter. Here is an example of a report generated by this method
for the first tab of the demo application.
The HtmlGenerator class in the lib/gui/Html.jag library script provides a more generic
framework for HTML report generation. Its use is demonstrated by the Quest sample
application in Chapter 8.
4.12.3 Generating Images
Another useful method of capturing the information presented by a GUI is to save it in an
image file. JavaGram provides the gui.saveImage() method for this purpose. This method
is also internally used by gui.toHtml() to save things like graphs in separate files that are
then referenced by the HTML file.
Another useful method for image capture is gui.imageAsHex() which, rather than saving
the image in a file, returns a hexadecimal string representation of it so that, for example,
it can be stored in a database.
4.13 Tips and Tricks
This section describes a number of programming tips that will come handy when
developing GUIs for real-life applications.
4.13.1 Using Boiler Plates
Most GUI elements extend <Comp> (see Chapter 11) which has a large number of
properties. If you encounter a situation where you’re setting these properties (e.g.,
GUI Programming
137
fgColor) to the same value for many elements then you can simplify your code by
factoring out the commonalities. To do this, set the relevant properties to the desired
values in a <Plate> element and then use this boiler plate when creating other elements.
For example, suppose that you have a panel that displays various fields for the user to fill,
where some of these fields are required and some are optional. You might decide to
highlight the required fields by setting their foreground color to red and assigning a
‘required’ tooltip to all such fields. This is nicely captured by a boiler plate.
<Plate Req fgColor="0xFF0000" tooltip="Required"/>
Now, when defining a component, you can base it on this boiler plate like this:
<Req:Label title="Surname"/>
//...
<Req:Field.date dob/>
For a good example of using boiler plates, see the Screen class in the lib/gui/Screen.jag
library script, where you’ll find this definition:
<Plate Hot enable={hotEnable()} event=hotHandler/>
This provides a very easy way of nominating the ‘hot’ fields in a screen. A change to any
such field results in gui.maintain() being invoked by the event handler to maintain the
state of the screen.
4.13.2 Managing Component Visibility
In most GUI frameworks (including Java’s Swing), the state of a component is managed
by procedural code. For example, if a search button is supposed to remain disabled until
the user specifies some search criteria, the program will contain explicit code that sets the
state of the button in response to events arising from search criteria specification. In
JavaGram, a declarative style is used instead – when defining the button, we state the
conditions under which it should be enabled/disabled and leave the rest to the framework.
The advantages should be obvious: less coding and a far more readable outcome.
Every element that extends <Comp> can have an enable and/or visible property. These two
properties can be set to arbitrary expressions. JavaGram evaluates these expressions in
response to certain events and, based on the outcome of the evaluation, changes the visual
state of the component accordingly.
The enable property determines whether the component should be displayed as enabled
or disabled (dimmed). If unspecified, the component is always enabled. The visible
property determines whether the component should be shown or hidden. If unspecified,
the component is always visible.
138
JavaGram Agile Development
Internally, JavaGram uses certain GUI events as trigger for evaluating these two
properties. Typical triggers include: the user pressing a button, clicking in a check box,
choosing something from a combo box, and so on. In response to any such event,
JavaGram propagates the event from its origin, up the GUI hierarchy until it can go no
further, and then down the hierarchy (evaluating these two properties wherever relevant)
until it covers all the children. This algorithm delivers the desired outcome in almost all
cases. If you encounter a situation where the framework’s behavior is insufficient, you
can force an explicit evaluation of these two properties by calling the gui.maintain()
method. Here is the signature of this method:
void gui.maintain (native comp, boolean force = false)
The component you pass as the first argument and all its direct and indirect descendents
will be processed. This method uses optimization measures when deciding whether to
evaluate the enable and visible property of each component. For example, for a disabled
container, it won’t attempt to process its descendents. You can override this optimization
by passing true for the force parameter.
4.13.3 Correct Use of Layouts
Layouts are fundamental to the way components are organized in a container. An
important benefit of layouts is that when a container is resized, its children are also
resized according to the properties of the container’s layout. Choosing the right layout
ensures that resizing results in the contents preserving their logical structure and visual
appeal.
We’ve seen the use of a number of layouts in this chapter. There are also a few that we
haven’t covered yet. Here is a list of all the layouts supported by JavaGram and a
summary description of their purpose.
Layout
<Layout.flow/>
<Layout.border>
<Layout.card/>
<Layout.grid/>
<Layout.gridBag/>
GUI Programming
Purpose
For organizing components left-to-right. This is the default layout
for a panel whose layout is unspecified. Use this if you want to
display a collection of buttons organized horizontally.
For organizing components in up to five groups, consisting of north,
south, east, west, and center. The center component is the dominant
one and suitable for a scroll pane containing a table, a list, or the
like. When you add a component to a container with border layout,
you can specify its destination group using the lay property of the
component. If you don’t, $center will be assumed.
For organizing components on top of each other, such that only one
is visible (at the top) at a time. Useful when you want a collection of
pre-loaded panels, but the user is intended to see only one of them.
For organizing components in a matrix, especially a group of very
similar components (e.g., buttons, images, sometimes even panels).
The grid must have a predetermined number of rows and columns.
Components are added in left-to-right and top-to-bottom order.
This is like the grid layout but much more flexible. The components
139
are not added directly but through <Lay> elements that determine the
position of each element as well as other properties such as amount
of space to take up. One of the flexibilities of a grid-bag layout is
that a component can span across grid boundaries and occupy
multiple rows and/or columns.
<Layout.horizontal/> For organizing components along a horizontal axis.
<Layout.vertical/>
For organizing components along a vertical axis.
4.13.4 Managing Periodic Tasks
If your application needs to do something periodically then a timer is required. JavaGram
provides two types of timers for such cases:

The Timer and TimerTask classes defined in lib/lang/Common.jag and described in
Chapter 6 are intended for anything that is not GUI related. This makes it suitable for
server-side usage, such as polling a table in a database and processing any new data
that may have been deposited there since the last poll.

<Timer> is intended for doing periodic tasks that are GUI related (e.g., causing
changes to the visual appearance of the GUI). An example would be polling the state
of the files in a directory and updating a tree that provides a visual representation of
the directory structure.
The reason for distinguishing between GUI and non-GUI periodic tasks is the underlying
design of JavaGram’s GUI framework which is based on Java’s Swing. Swing requires
any code that accesses GUI components to run on Swing’s event dispatching thread. The
<Timer> element conforms to this requirement, but Timer doesn’t. So if you want to avoid
obscure threading and event handling issues, use the appropriate type of timer for the task
at hand.
To illustrate the use of <Timer>, we’ll add a small date and time panel to the bottom of our
demo application and refresh it every second.
<Panel clockPanel lay=$south visible=false>
<Field.text clock readOnly=true width=150 align=$center/>
</Panel>
Here is the timer and its event handler.
<Timer timer event=timerHandler delay=1000/>
protected void timerHandler (native comp, symbol event) {
if (event == $do)
clock.value = sys.format(sys.date(), "dd MMM yyyy, HH:mm:ss");
}
public void showClock (boolean show) {
clockPanel.visible = show;
timer.start = show;
140
JavaGram Agile Development
timer.stop = !show;
}
The timer’s delay property is specified in milliseconds and specifies how often it should
fire. The timer doesn’t start until its start property is set to true, and eventually stops
when its stop property is set to true. When the timer fires, it raises a $do event, in
response to which we update the clock field value.
We’ve also added a check box to the application’s toolbar to control the visibility of the
clock (initially not visible).
<Option.tick clock title="Show Clock"
action={DemoApp.singleton.showClock(clock.select)}/>
Here is the application appearance when the clock is active.
4.13.5 Using Worker Threads
Performing a lengthy task within the even handler of a component will cause the GUI to
freeze until the task is completed. For example, if the user presses a ‘search’ button to
perform a potentially long database query, the application will become unresponsive until
the button’s action handler returns. Whilst a delay of a few seconds is tolerable, longer
delays, especially running into minutes, will degrade the user experience.
The freezing of the application during lengthy operations can be avoided by handing over
the lengthy task to a separate thread (threads are introduced in Chapter 6). However, as
with Timer, normal threads are not suitable for tasks that may access the GUI
GUI Programming
141
components. The <Worker> element provides the right solution for such cases, because it
executes on Swing’s event dispatcher thread.
As an example, we refer to the login procedure of the Quest sample application described
in Chapter 8. When this application is run, it displays a small frame containing the
authentication fields. Once login is completed, this frame is replaced by the actual
application frame. When the user enters their username and password and presses the
Login button, we use a worker thread to perform the login, because it can potentially be a
lengthy operation. While login is in progress, we want to show a progress bar and also
keep the frame responsive so that, for example, the user can cancel the login. The worker
thread makes all this possible.
Here is how the worker thread is defined in Quest.
<Worker login worker=loginWorker/>
The worker property denotes the name of a method in the same class (similar to an event
handler) that’s internally called by JavaGram.
protected vague loginWorker (symbol cmd, vague val) {
switch (cmd) {
case $do:
setLoggedIn($pending);
progress.indefinite = true;
try {
setCurrUser(User.login(username.value@string, password.value@string));
setLoggedIn($yes);
return getCurrUser();
}
catch (Exception e) {
setLoggedIn($no);
progress.indefinite = false;
gui.alert("Login Failed", e.getMessage(), frame, $warning);
}
break;
case $done:
if (loggedIn == $yes) {
// Replace the login panel with application panel:
frame.visible = false;
frame -= panel;
sys.call(Util.getSingleton("quest/gui/AppPanel"), $addToFrame, frame);
}
break;
case $progress:
break;
case $process:
break;
}
142
JavaGram Agile Development
return null;
}
The worker commences execution when its run property is set to true. The $do command
is raised as a result, allowing loginWorker() to react – we handle this by activating the
progress bar and sending a login request to the application server. When the worker
completes execution, the $done command is raised. In response to this, we check if the
login has been successful and, if so, reconfigure the frame to show the application. If
required, the worker can be easily cancelled by setting its cancel property to true.
The $progress command is raised when a value is assigned to the progress property of
the worker, in which case val denotes the progress value. This gives you the opportunity
to visually display the level of progress so far (which we haven’t done here because
we’ve used an indefinite progress bar).
The $process command is raised when a value is assigned to the publish property of the
worker. It’s possible that $process is raised once for multiple assignments. Therefore, val
is always a vector of the latest assigned values. This gives you the opportunity to process
data published so far (again, we haven’t used this is our example).
4.13.6 Procedural Creation of Components
As stated at the beginning of this chapter, JavaGram provides a declarative style of GUI
programming. This is very convenient when you can determine the structure of your user
interface in advance and define it accordingly. But what about situations where the
components can’t be determined in advance and are instead determined through user
actions?
We’ve already seen one example of this in our discussion of grids, where the user causes
a new row to be added to the grid by clicking on a link. Our approach there was to
capture the definition of the new row in a separate class and then add it to the grid using
the += operator. This approach is recommended in situations where it delivers a clean
solution (as was the case in our grid example).
There is, however, the odd situation where a procedural approach is preferred because it
involves the least amount of mocking around. For example, if you have an application
that’s supposed to read some meta data and use it to build a customized panel then a
procedural approach is likely to produce a simpler solution.
JavaGram allows you to define a GUI component procedurally using the gui.create()
method, whose signature is as follows.
native gui.create (symbol tag, map<symbol,vague> props = null)
The first parameter denotes the GUI element’s name as a symbol (e.g., $Node). If the
name is qualified, you should escape the symbol (e.g., ${Pane.scroll}). The second
GUI Programming
143
parameter is optional and can be used to specify the element’s properties as a collection
that maps property names (each specified as a symbol) to their values.
A very common use of gui.create() is for dynamically adding nodes to a tree. For
example, to add a child node titled ‘Sample’ to an existing node denoted by the node
variable, you can write:
node += gui.create($Node, [$title=>"Sample"]);
There are also two companion methods for getting and setting the properties of GUI
components dynamically. For example, to get/set the title of a tree node, you can write:
string title = gui.get(node, $title);
gui.set(node, $title, "New Title");
// title = node.title
// node.title = "New Title"
4.14 Browser-based GUIs
The GUI elements described in this chapter (with the exception of <Graph.flow> and
<Jade.editor>) are also supported by the Flash version of the JavaGram runtime.
However, please note that because Flash is asynchronous and single-threaded, a few
features behave slightly differently:

<Worker> executes in the same thread, not a separate thread.

In <Alert>, <Alert.ync>, <FileChooser>, <ColorChooser>, the show property always
returns null, rather than the result of the selection. Similarly, gui.alert() returns
null. For these cases, you can set the result property of the element to a callback
method that will be passed the result when it becomes available.

<Area.url> provides very basic HTML support.
The Demo application presented in this chapter will run in both the Java and the Flash
runtime. Here is what it looks like when run in a browser.
144
JavaGram Agile Development
4.15 Summary of Elements
We’ll end this chapter by providing a visual summary of JavaGram’s built-in GUI
elements as a UML diagram. For a detailed description of each element, please refer to
Chapter 11.
GUI Programming
145
146
JavaGram Agile Development
5 SQL Programming
A common requirement of most applications is the persistence of objects to external
storage so that they can be reconstructed in a subsequent run. For some objects,
persistence to a local file would be adequate. For example, user ‘preferences’ can be
saved to a file on the user’s machine and retrieved the next time the application is run.
For objects that can be shared by multiple users, however, this approach is inadequate as
it doesn’t deal with concurrency issues. It’s also inefficient when a large number of
objects are involved. A relational database is typically used in such cases, where objects
are stored in indexed tables and accessed through the SQL language.
This chapter describes the JavaGram features aimed at SQL programming. For ease of
understanding, we’ll use simplistic examples that illustrate the approach rather than
provide an exhaustive treatment. You should study this chapter in conjunction with the
SQL Reference presented in Chapter 12. Also, the Quest sample application presented in
Chapter 8 provides more elaborate examples of the concepts presented here.
In JavaGram, all database access is through Java DataBase Connectivity (JDBC). For
ease of use and productivity, JavaGram hides the low-levels of JDBC. The sql and bom
pseudo classes provide access points that blend well with JavaGram features such as
containers. The Object library class provides a further level of abstraction that enables
you to quickly implement persistence through inheritance.
5.1 Working with Databases
An application that performs database interaction must perform these steps:

Load a JDBC driver for the database

Connect to the database

Perform any number of queries and/or transactions

Disconnect from the database
This section describes the first two and the last step. The third step is described in
Sections 5.3 and 5.4.
The database engine we’ve used throughout this book is MySql. You should download it
from www.mysql.com, install, and configure it before attempting the examples presented
in this chapter. For simplicity, our examples assume that you’ve configured the password
for the ‘root’ account to be ‘password’.
SQL Programming
147
5.1.1 Explicit Connection
A database connection can be managed in one of two ways: explicitly by the programmer
or implicitly by JavaGram. We strongly recommend the latter, as it involves less coding
and is much easier to manage. The explicit style is described here for completeness.
The following class illustrates the explicit style of managing a database connection.
<jag domain="doc/code/chap5">
class ExplicitStyle {
static final string DB_URL = "jdbc:mysql://localhost/mysql";
public static void main () {
sql.loadDriver("com.mysql.jdbc.Driver");
native conn = sql.connect(DB_URL, "root", "password");
query (conn) {
native rs = sql.execQuery(conn, "select user, host from user");
while (sql.next(rs))
sys.println(sql.getRow@map(rs));
}
sql.close(conn);
}
}
</jag>
The sql.loadDriver() method takes a qualified Java class name denoting the JDBC
driver and attempts to load it. The driver’s JAR file must be in your Java CLASSPATH
for this to work. For example, you can specify the driver’s JAR file along with JAG.jar in
the command line for running a program:
java -cp "C:/JavaGram/jar/JAG.jar;C:/JavaGram/jar/mysql-connector.jar"
jag.run.Env -root "C:/JavaGram" "doc/code/chap5/ExplicitStyle.jag"
A connection is created by calling sql.connect(). The first argument to this method is the
URL of the database you want to connect to. For this, we’ve used the ‘mysql’ database
(MySql keeps a database about itself to store configuration information). The second and
third arguments specify a valid username and password for the connection.
Next, we perform a query using the query statement (don’t worry about the details for
now; they’ll be explained later), and finally close the connection by calling sql.close().
The query in this example retrieves the user and host attributes from the user table and
displays them, producing the following output.
[$host=>"localhost", $user=>"root"]
5.1.2 Implicit Connection
In the implicit connection style, details such as database driver, URL, and login
information are centrally stored in an application configuration file. This configuration
148
JavaGram Agile Development
file is nominated in the command line for running the program using the -config option.
Application configuration files are described in Chapter 7, and can hold many other
pieces of information (all specified as a map) about the application.
Here is a minimal configuration file for capturing database information.
[
$database=> [ $mySqlDb=>[
,
,
,
,
,
]
]
]
$name=>"MySql Database"
$url=>"jdbc:mysql://localhost/mysql"
$user=>"root"
$password=>"password"
$timeout=>[$login=>30, $query=>20]
$driver=>"com.mysql.jdbc.Driver"
The $database key in the configuration map points to a collection that maps a key (e.g.,
$mySqlDb) for each database used by the application to its configuration. The latter is itself
a map that captures things like the driver class name, database URL, login information,
etc. The example here is intended to give us access to the same database we used for the
explicit connection style.
Using this configuration file, the ExplicitStyle class can be re-written as:
<jag domain="doc/code/chap5">
class ImplicitStyle {
public static void main () {
query ($mySqlDb) {
native rs = sql.execQuery($mySqlDb, "select user, host from user");
while (sql.next(rs))
sys.println(sql.getRow@map(rs));
}
}
}
</jag>
As you can see, we no longer have to explicitly load a driver or open/close a connection –
all that is managed by JavaGram. Note that in the query statement and the call to
sql.execQuery(), we pass the database key specified in the configuration file (i.e.,
$mySqlDb) rather than a connection. An added benefit of this approach is that, because
database information is centrally captured, making changes to it becomes trivial and
eliminates any potential impact on the application code.
Here is a sample command line for running this program:
SQL Programming
149
java -cp "C:/JavaGram/jar/JAG.jar;C:/JavaGram/jar/mysql-connector.jar"
jag.run.Env -root "C:/JavaGram" –config "doc/code/chap5/FirstConfig.jag"
"doc/code/chap5/ImplicitStyle.jag"
The program produces exactly the same output as ExplicitStyle.
5.2 Text Members
Before we get into transactions and queries, we’d like to introduce a JavaGram feature
that comes very handy for composing SQL commands.
You’ve already seen three kinds of class members: fields, methods, and GUI members.
There is also a fourth kind called text members. Like GUI members, these are defined
using a markup notation, but unlike them, text members behave like methods. You can
think of a text member as a parameterized string, but with the added flexibility that the
string can span across multiple lines and not requiring double-quote characters to be
escaped. Examples of situations where text members prove useful include: report
generation and SQL composition. Of course, the same outcome can be achieved by
concatenating strings, but text members are superior because they’re far more readable
and give you more control. Their use for formulating SQL has the added benefit of
separating SQL code from normal method code, which further simplifies future
maintenance.
5.2.1 Text
A text member is defined using the <text> markup. For example, suppose that you have a
program that creates user accounts and emails the account details to each new user. The
email text can be composed by a text member.
<jag domain="doc/code/chap5">
class Emailer {
static <text string newUserEmail (string name, string username, string password)>
Dear {name},
Please be advised that your new user account has been setup for accessing
our web site. Your account details are:
Username: {username}
Password: {password}
Please visit www.acme.com and login with these details.
Upon login, you'll be asked to change your password.
</text>
public static void main () {
sys.println(newUserEmail("John Smith", "john.smith", "secret"));
}
}
</jag>
Note how the newUserEmail() text member is defined to return a string. This return type
is mandatory because a text member always returns a string value. Also note how
150
JavaGram Agile Development
references to method parameters within the text are enclosed in braces. This follows the
same splicing rules as in delayed strings, so you can enclose any valid expression in
braces.
The semantics of a text member is very simple. It’s invoked like a normal method, text
substitution is performed, and the resulting text is returned as the final value.
When run, this program produces the following output.
Dear John Smith,
Please be advised that your new user account has been setup for accessing
our web site. Your account details are:
Username: john.smith
Password: secret
Please visit www.acme.com and login with these details.
Upon login, you'll be asked to change your password.
Like GUI members, text members accept properties; these should appear after the method
signature. For example, if you set the indent property to true, the text indentation (as
appearing in the code) is preserved.
5.2.2 Text.sql
JavaGram provides a specialization of <text> for the specific purpose of composing SQL.
The markup tag for this is <text.sql>. It behaves the same as <text> but supports these
conventions for escaping spliced values.

A backquote (`) before a spliced expression means that the resulting value should be
escaped as required by SQL (e.g., {`username}).

An equal symbol (=) before a spliced expression means that the resulting value should
be used verbatim (e.g., {=tableName}).

Other than the above two scenarios, the {...} syntax is not allowed in order to avoid
inadvertent misuse.
There are also a number of specializations of <text.sql> that target specific types of SQL
operations:

<text.sql.query> composes and performs a query, returning a native result set.

<text.sql.update> composes and performs an update, returning an integer that
denotes the number of rows affected.

<text.sql.prepare> creates a parameterized prepared statement that can later be
instantiated multiple types. It returns the native prepared statement.

<text.sql.callable> creates a callable statement for invoking a stored procedure. It
returns the native statement.
SQL Programming
151
We’ll see examples of their use later in this chapter.
5.3 Transactions
A transaction is an operation that alters the information held by a database. Examples
include: creating a new table, inserting rows into a table, updating an existing table row,
or deleting table rows.
One of the benefits of using a database engine is the predictability of transactions. If
multiple programs attempt to update the same information, the database isolates these
updates so that they don’t trip on one another.
5.3.1 Creating a Database
For the rest of this chapter, we’ll use a test database for the purpose of the sample codes
provided. To create this database, run the MySql Query Browser, right-click on the
Schemata tab, and choose Create New Schema from the popup menu. A dialog appears;
enter ‘test’ and press OK.
At this point, the test database is empty, but we’ll run transactions to populate it shortly.
5.3.2 Creating Tables
Let’s create our first table. Here is a modified version of the Person class introduced
earlier in the book, but now written with persistence in mind.
class Person {
int id;
string firstName;
string lastName;
string sex;
date dob;
boolean married;
public static void createTable () {
string str = "create table if not exists person("
+ "id integer not null,"
+ "first_name varchar(16),"
+ "last_name varchar(16),"
+ "sex varchar(8),"
+ "dob date,"
+ "married bit,"
+ "primary key(id), index(last_name))";
transaction ($testDb) {
sql.execUpdate($testDb, str);
}
}
public static void main () {
createTable();
}
152
JavaGram Agile Development
}
The createTable() method composes a SQL string for creating a person table. The
attributes of this table are defined to match the Person class fields. We’ve also defined
two indices for the table – one for the id column (defined as a primary key) and one for
the last_name column. The benefit of an index is that it enables fast lookup, so it’s
typically defined for columns that are frequently accessed. However, indices also come at
a cost – they consume additional storage and the database has to keep them up-to-date
when transactions are performed. So their use must be treated as a tradeoff.
To create the table, we pass the SQL string to sql.execUpdate(). Given that this is a
transaction, we must perform the call inside a transaction statement. The latter marks the
transaction boundary and ensures that within that boundary a valid database connection is
obtained, used, and released.
Once you’ve run this program, use the MySql Query Browser to check that the person
table has been added to the test database.
Returning to the createTable() method, the way the SQL string is composed is typical of
the way it’s done in most programming languages (including Java). This approach is
crude and problematic. For someone reading a program, it makes it rather difficult to
locate SQL code (because it can potentially be anywhere) and to readily make sense of it
(because it’s built procedurally out of smaller fragments). The JavaGram <text.sql>
element provides a neat solution to this problem, as evidenced by the following re-write
of the Person class.
class Person {
//...
public static void createTable () {
transaction ($testDb) {
sql.execUpdate($testDb, createTableSql());
}
}
static <text.sql string createTableSql()>
create table if not exists person(
id integer not null,
first_name varchar(16),
last_name varchar(16),
sex varchar(8),
dob date,
married bit,
primary key(id), index(last_name))
</text.sql>
public static void main () {
createTable();
}
}
SQL Programming
153
Organizing all your SQL like this will totally decouple it from normal program code, thus
enhancing the clarity of your design. Subsequently, a simple global search on ‘<text.sql’
will pinpoint all SQL code in your program.
For this example, we can go one step further and use <text.sql.update>.
class Person {
//...
static <text.sql.update native createTable () db=$testDb>
create table if not exists person(
id integer not null,
first_name varchar(16),
last_name varchar(16),
sex varchar(8),
dob date,
married bit,
primary key(id), index(last_name))
</text.sql.update>
public static void main () {
transaction ($testDb) {
createTable();
}
}
}
Here, createTable() not only composes the SQL but also executes it. Therefore, it needs
to have a database connection, which is provided via the db property.
5.3.3 Dropping Tables
Dropping a table causes it to be permanently removed from the database, including any
data it’s holding. This is easily expressed using a <text.sql.update> method. For
example, for the Person table, we can write:
static <text.sql.update native dropTable () db=$testDb>
drop table if exists person
</text.sql.update>
In fact, it’s just as easy to write a generic method that will work for any table by passing
the table name as a parameter:
static <text.sql.update native dropTable(string table) db=$testDb>
drop table if exists {=table}
</text.sql.update>
5.3.4 Inserting Rows
The following method inserts a row into the person table.
154
JavaGram Agile Development
<text.sql.update int insert () db=$testDb>
insert into person (id, first_name, last_name, sex, dob, married)
values ({`id}, {`firstName}, {`lastName}, {`sex}, {`sys.format(dob)}, {`married})
</text.sql.update>
Because this method is not static, it has access to Person fields. Note how the reference to
each field has been expressed as {`field}, instructing JavaGram to escape the value
appropriately. For the dob field, we’ve used the sys.format() method to format the date
according to the syntax required by MySql (e.g., '1982-2-23').
Here is a sample call to this method:
transaction ($testDb) {
Person p = [@Person id=>1, firstName=>"John", lastName=>"Smith",
sex=>"Male", dob=>[#1982-2-23], married=>false];
p.insert();
}
The call to insert() returns 1, which is the number of rows inserted.
If you want to insert many rows into a table, you might want to consider using a
prepared statement. The advantage of a prepared statement is efficiency – it’s parsed
only once by the database engine and reused as often as required. Here is an example:
singleton class People {
Person p;
int count;
<text.sql.prepare native insertPrep () db=$testDb>
insert into person (id, first_name, last_name, sex, dob, married)
values ({?p.id}, {?p.firstName}, {?p.lastName}, {?p.sex},
{?sys.format(p.dob)}, {?p.married})
</text.sql.prepare>
public void personTest () {
transaction ($testDb) {
native insert = insertPrep();
p = [@Person id=>1, firstName=>"Linda", lastName=>"Black", sex=>"Female",
dob=>[#1980-12-20], married=>true];
sql.execUpdate(insert);
p = [@Person id=>2, firstName=>"Mark", lastName=>"Adams", sex=>"Male",
dob=>[#1988-02-13], married=>false];
sql.execUpdate(insert);
}
public static void main () {
People.singleton.personTest();
}
}
SQL Programming
155
Take note of the way the SQL in insertPrep() is formulated. The notation {?expr} is
specific to prepared statements – it specifies placeholders that are substituted for each
time the statement is reused. When insertPrep() is called, SQL is formulated and
returned as a prepared statement but not executed. The execution occurs later on when
sql.execUpdate() is invoked on the prepared statement. The code therefore is written
such that, for example, {?p.id} is valid at the time of execution, not preparation.
Understanding this subtle point is vital to the proper use of prepared statements.
The personTest() method exercises the prepared statement by first creating it and then
using it twice. Before each update, we set p to the Person object to be inserted and then
call sql.execUpdate(), passing just the prepared statement to the latter.
An alternative to using <text.sql.prepare> is to use sql.prepare(). To illustrate its use,
here is an alternative method of inserting a Person:
public static void insertPersonDynamic (Person p) {
string str = "insert into person (id, first_name, last_name, sex, dob, married) "
+ "values (?, ?, ?, ?, ?, ?)";
native ps = sql.prepare($testDb, str);
sql.set(ps, 1, p.id);
sql.set(ps, 2, p.firstName);
sql.set(ps, 3, p.lastName);
sql.set(ps, 4, p.sex);
sql.set(ps, 5, p.dob);
sql.set(ps, 6, p.married);
sql.exec(ps);
}
Note how the ? placeholders are later set using sql.set() and the prepared statement
executed using sql.exec(). In general, <text.sql.prepare> is preferred due to its
conciseness and simplicity, but it’s less flexible. Therefore, use sql.prepare() only for
cases where you need to form the SQL dynamically.
5.3.5 Updating Rows
Once a row has been inserted into a table, it may subsequently need to be updated. For
example, if a person’s marital status changes then we’ll need to update the married
column. Here is how we could define Person.update():
<text.sql.update int update () db=$testDb>
update person
set first_name = {`firstName},
last_name = {`lastName},
sex = {`sex},
dob = {`sys.format(dob)},
married = {`married}
where id = {`id}
</text.sql.update>
156
JavaGram Agile Development
For example, to change a person’s last name, we would write something like:
transaction ($testDb) {
//...
p.lastName = "Anderson";
p.update();
}
Again, if we were to update many rows of the person table, it would be wise to
implement this as a prepared statement.
<text.sql.prepare native updatePrep () db=$testDb>
update person
set first_name = {?p.firstName},
last_name = {?p.lastName},
sex = {?p.sex},
dob = {?sys.format(p.dob)},
married = {?p.married}
where id = {?p.id}
</text.sql.prepare>
This would be used, for instance, like this:
transaction ($testDb) {
native update = updatePrep();
//...
p.lastName = "Anderson";
sql.execUpdate(update);
}
5.3.6 Deleting Rows
Deleting a table row is straightforward. Like an update, we need to specify the criteria for
identifying the rows that need to be deleted. For example, the following method deletes a
person row via its unique id.
static <text.sql.update int deleteById (int id) db=$testDb>
delete from person
where id = {`id}
</text.sql.update>
As before, the same can be expressed as a prepared statement if it’s likely to be used
often.
5.4 Queries
A database query is a read-only operation. It typically involves searching one or more
tables, and will ultimately return a result set. The result set is then processed by, for
SQL Programming
157
example, iterating through it, processing each result (which is often the equivalent of a
row), and converting it to a JavaGram data type, such as a map or object.
The easiest way to write a query is to express it as a <text.sql> or <text.sql.query> class
member.
5.4.1 Retrieving Rows
The most common type of query involves the retrieval of table rows. As a simple
example, here is a method, Person.find(), that retrieves a Person using its unique ID.
public static Person find (int id) {
native rs = _findById(id);
return sql.next(rs) ? sql.getRow@Person(rs) : null;
}
protected static <text.sql.query native _findById (int id) db=$testDb>
select * from person
where id = {`id}
</text.sql.query>
We’ve implemented this as two methods: _findById() provides and executes the SQL,
which is in turn called by find(). The former returns a result set, which the latter
examines by calling sql.next() to see if it has any data, in which case sql.getRow() is
called on the result set to retrieve the row. Note the strange casting syntax we’ve used
here rather than the normal casting syntax. Here is the difference:

sql.getRow(rs)@Person is the normal casting syntax but not suitable for this situation
– the cast happens too late.

sql.getRow@Person(rs) is the syntax we want here because it tells sql.getRow() that it
should retrieve the row as a Person object. This is called method casting and can be
applied to any pseudo class method that returns a non-void type. However, it’s up to
the method to decide what to do with it (most methods treat it the same way as
normal casting). Method casting provides an easy way of specifying the desired
syntax of a result. For example, we can get the result as a map instead by writing:
sql.getRow@map(rs).
The call Person.find(1) will return the following:
[@Person dob=>[#1980-12-20], firstName=>"Linda", id=>1, lastName=>"Black",
married=>true, sex=>"Female"]
Note how underscores in table column names have been automatically translated (e.g.,
first_name becomes firstName). JavaGram does this as a rule because underscores are
commonly used in data models, whereas in JavaGram we prefer to use camel-case
identifiers.
Here is another example, which returns a vector of all persons in the person table.
158
JavaGram Agile Development
public static vector<Person> find () {
native rs = _findAll();
vector<Person> vec @= vector();
while (sql.next(rs))
sys.append(vec, sql.getRow@Person(rs));
return vec;
}
protected static <text.sql.query native _findAll () db=$testDb>
select * from person
</text.sql.query>
Because of the wildcard query, we expect the result set to potentially contain multiple
rows, so we’ve used a while-loop to iterate through the result set and incrementally add
each result to a vector. The call Person.find() will return the following:
[ [@doc\code\chap5\Person dob=>[#1980-12-20], firstName=>"Linda", id=>1
,lastName=>"Black", married=>true, sex=>"Female"]
, [@doc\code\chap5\Person dob=>[#1988-02-13], firstName=>"Mark", id=>2
,lastName=>"Adams", married=>false, sex=>"Male"]
]
As with updates, frequently used queries would be better expressed using prepared
statements, preferably using <text.sql.prepare> or, where the SQL must be formed
dynamically, using sql.prepare().
5.4.2 Processing a Result Set
As shown in earlier examples, to process a result set returned by a query, we typically use
a loop and keep calling sql.next() until it returns false. There are three other related
methods that allow you to move through the result set:

sql.previous() moves to the previous record in the result set.

sql.first() moves to the first record in the result set.

sql.last() moves to the last record in the result set.
All these methods return true when successful and false otherwise. Use them to process
query results in your preferred order. For example, if you’re searching a large result set
for a specific record and expect it to be in the tail end of the result set, it makes sense to
move to the end of it by calling sql.last() and then iterate back by calling
sql.previous().
5.4.3 Retrieving Attributes
If you need to retrieve a single attribute of a table then sql.getRow() is overkill and rather
wasteful. You can use sql.get() for such cases instead. It allows you to retrieve an
attribute using its column name or its one-based column index. The following method,
SQL Programming
159
Person.testGet(), demonstrates this by retrieving last_name (via its column name) and
dob (via its column index):
public static void testGet () {
native rs = _findById(1);
if (sql.next(rs)) {
sys.println("Last name: ", sql.get(rs, $last_name));
sys.println("DOB: ", sql.get(rs, 5));
// Fifth column is dob
}
}
Note that, unlike sql.getRow(), sql.get() does not perform any column name translation.
This example produces the following output:
Last name: Black
DOB: 1980-12-20
Of course, if your intention is to retrieve just one column then the SQL can be better
written to reflect your intention. For example:
select last_name from person where id = 1
5.4.4 Counting Rows
To efficiently count the number of rows in a table, use the MySql count function, as
illustrated by this method:
public static int count () {
native rs = sql.execQuery($testDb, "select count(*) from person");
return sql.next(rs) ? sql.get@int(rs, 1) : 0;
}
Note how we’ve used method casting on sql.get() because this method has a vague
return type. In this case, normal casting would have worked just as well.
5.4.5 Performing Joins
A join is a query that operates on two or more tables, exercising a relationship between
the two tables. For example, suppose that we have an Account class defined as follows.
<jag domain="doc/code/chap5">
class Account {
string accNo;
string accType;
int owner;
// ID of Person who owns the account
static <text.sql.update native createTable() db=$testDb>
create table if not exists account(
acc_no varchar(16) not null,
acc_type varchar(16),
160
JavaGram Agile Development
owner integer not null,
primary key(acc_no), index(owner))
</text.sql.update>
static <text.sql.update native dropTable() db=$testDb>
drop table if exists account
</text.sql.update>
<text.sql.update int insert () db=$testDb>
insert into account (acc_no, acc_type, owner)
values ({`accNo}, {`accType}, {`owner})
</text.sql.update>
}
</jag>
The account.owner column refers to the person.id column. Therefore, there is a one-tomany relationship between person and account tables (a person can have multiple
accounts). The owner column is said to be a foreign key, because it refers to the key of
another table.
To illustrate the use of joins, let’s populate the account table with some data first.
public static void populateAccount ()
transaction ($testDb) {
vector<Account> accounts = [
[@Account accNo=>"0001",
,[@Account accNo=>"0002",
,[@Account accNo=>"0003",
];
for (Account acc in accounts)
acc.insert();
}
}
{
accType=>"Cheque", owner=>1]
accType=>"Saving", owner=>1]
accType=>"Saving", owner=>2]
Now, suppose that we want to retrieve the name of every person who has an account, as
well as the account number and type. Here is how we’d express such a query.
protected static <text.sql.query native join() db=$testDb>
select first_name, last_name, acc_no, acc_type
from person, account where person.id = account.owner
</text.sql.query>
The following method exercises the join.
public static void joinTest () {
query ($testDb) {
native rs = join();
while (sql.next(rs))
sys.println(sql.getRow@map(rs));
}
}
SQL Programming
161
It produces the following output.
[$accNo=>"0001", $accType=>"Cheque", $firstName=>"Linda", $lastName=>"Black"]
[$accNo=>"0002", $accType=>"Saving", $firstName=>"Linda", $lastName=>"Black"]
[$accNo=>"0003", $accType=>"Saving", $firstName=>"Mark", $lastName=>"Adams"]
5.5 Calling Stored Procedures
Most database servers (including MySql, as of version 5.0) support stored procedures
(and stored functions). A stored procedure is a set of SQL statements stored in the
database server for subsequent reuse. The advantage is runtime efficiency – once a
procedure is defined, you no longer have to issue its SQL to the server, but simply refer
to the stored procedure instead.
To illustrate the creation and calling of stored procedure, let’s define a simple stored
procedure named count_people() that counts the number of rows in the person table and
returns this in an out parameter. We’ll also define a method for dropping the stored
procedure. Both these are defined as members of the Person class:
static <text.sql.update native createCountProc () db=$testDb>
create procedure count_people (out rows int)
begin
select count(*) into rows from person;
end
</text.sql.update>
static <text.sql.update native dropProc () db=$testDb>
drop procedure if exists count_people
</text.sql.update>
To call the stored procedure, we can use a <test.sql.callable>, as shown below.
singleton class People {
int count;
//...
public void procCallTest () {
query ($testDb) {
native callable = callCountPeople();
sql.registerOutParam(callable, 1, "INTEGER");
sql.exec(callable);
sys.println("Person count: ", count);
}
}
<text.sql.callable native callCountPeople () db=$testDb>
call count_people({?count})
</text.sql.callable>
public static void main () {
transaction ($testDb) {
162
JavaGram Agile Development
//...
Person.dropProc();
Person.createCountProc();
}
People.singleton.procCallTest();
}
}
callCountPeople() returns a callable statement. We use sql.registerOutParam() to
register the output parameter of count_people(), using its one-based index and expected
type. Finally, we call sql.exec() to perform the call.
5.6 Streamlined Data Modeling
Earlier, we looked at examples of providing persistence for the Person class. As you
analyze your problem domain, other classes emerge, some of which would have
relationships with Person. We’ve already seen an example of this (Account). Another
obvious one would be Address to capture a person’s address information.
If we were to continue with traditional entity relationship (ER) modeling, we would also
provide a table for Address and one for capturing the one-to-many relationship between
Person and Address, or have a foreign key in the address table to depict a person (as we
did with Account) This style of ER modeling, while offering the greatest SQL
composition flexibility, is rather time consuming and maintenance intensive – changes in
the object model (e.g., adding a new field to Person) would typically require changes to
the data model. Agile development requires a smarter approach.
Suppose that our problem domain is a CRM application. We might conclude that the only
retrieval scenarios required for a customer are via ID, last name, or date of birth. We
might also conclude that addresses never need to be handled outside the context of a
customer. This enables us to capture customer data in just one very simple table.
<jag domain="doc/code/chap5">
class Address {
string street;
string town;
string state;
string zip;
}
class Customer {
int id;
string firstName;
string lastName;
date dob;
string sex;
vector<Address> addresses @= vector();
public static <text.sql.update int createTable () db=$testDb>
create table if not exists customer (
SQL Programming
163
id integer not null,
last_name varchar(16) not null,
dob date,
details text,
primary key (id), index (last_name, dob)
)
</text.sql>
static <text.sql.update native dropTable() db=$testDb>
drop table if exists customer
</text.sql.update>
public <text.sql.update int insert () db=$testDb>
insert into customer (id, last_name, dob, details)
values ({`id}, {`lastName}, {`sys.format(dob)}, {`sys.serialize(this)})
</text.sql.update>
public void addAddress (Address addr) {
sys.append(addresses, addr);
}
static public Customer getById (int id) {
query ($testDb) {
native rs = _getById(id);
if (sql.next(rs))
return sys.parse(sql.get(rs, $details)@string) @ Customer;
}
return null;
}
private static <text.sql.query native _getById (int id) db=$testDb>
select details from customer
where id = {`id}
</text.sql.query>
static public void main () {
transaction ($testDb) {
dropTable();
createTable();
Customer cust = [@Customer id=>1, firstName=>"John", lastName=>"Smith",
dob=>[#1992-10-22], sex=>"Male", addresses=>[]];
cust.addAddress([@Address street=>"24 Mark St", town=>"Balwyn",
state=>"Vic", zip=>"3103"]);
cust.addAddress([@Address street=>"15 High St", town=>"Kew",
state=>"Vic", zip=>"3105"]);
cust.insert();
}
sys.println(getById(1));
}
}
</jag>
The createTable() method creates a table where the first three columns capture the
indexed attributes. insert() inserts a Customer instance into the table. Note how
sys.serialize(this) is used to serialize the entire object into the last column.
164
JavaGram Agile Development
Conversely, getByID() uses sys.parse() to reconstruct the Customer object by parsing the
text in the last column.
The program produces the following output:
[@Customer addresses=>[[@Address state=>"Vic", street=>"24 Mark St",
town=>"Balwyn", zip=>"3103"], [@Address state=>"Vic", street=>"15 High St",
town=>"Kew", zip=>"3105"]], dob=>[#1992-10-22], firstName=>"John", id=>1,
lastName=>"Smith", sex=>"Male"]
The maintenance advantage of this approach should be obvious: adding new fields to the
Customer class or the Address class will not affect the data model and will have minimal
impact on the code.
For lack of a better term, we call this approach streamlined data modelling. The initial
reaction of experienced ER practitioners to this approach is often dismissive: ‘this goes
against normalization and won’t perform.’ Analysing user requirements will quickly
reveal where the performance hot spots are, and these can be easily accommodated
through traditional ER modelling. For everything else, a streamlined approach will
deliver a rapid, inexpensive, and easy-to-maintain solution.
We can quantify this in rough terms. If you have a class that is likely to have millions of
persistent instances that require to be retrieved in a number of different ways involving its
relationships to other classes (i.e., needing SQL joins) then traditional ER modelling is
your best bet. On the other hand, a class whose instances are in the thousands, or one that
doesn’t involve complex relationship-based retrievals, is adequately served by the
streamlined approach.
There is another angle to the performance argument that often gets overlooked. A
traditional, narrow view of performance sees it purely in terms of code execution speed.
A broader, agile view would see it in terms of both development cycle and execution
speed. Programs are not things that are developed once and used for years to come. They
take much time to develop and even greater time and effort to keep operational while
accommodating evolving requirements. Ignoring development speed in the overall
equation is therefore unrealistic.
5.7 Business Object Model
The Business Object Model (bom) pseudo class provides a convenient mechanism for
defining simple business objects that require persistence in a database. To define a
persistent business object, you subclass the generic library class lib/bom/Object and
override some of its method. The persistence mechanics are managed by Object,
eliminating the need for you to implement them directly and thus facilitating significant
development productivity.
5.7.1 Subclassing Object
Let’s redefine the Customer class of Section 5.6 as a subclass of Object.
SQL Programming
165
<jag domain="doc/code/chap5">
<load>
"lib/bom/Object"
</load>
class Address {
setable string street;
setable string town;
setable string state;
setable string zip;
}
class CustDetails {
getable int id;
setable string firstName;
setable string lastName;
setable date dob;
setable string sex;
}
class Customer extends CustDetails, Object {
vector<Address> addresses @= vector();
static {
Object.defineTable(Customer,
[ $db => $testDb
, $table => $customer
, $columns =>
[ $id => $id
, $last_name => $lastName
, $dob => $dob
, $rest => [$fields=>[$firstName, $sex, $addresses], $compress=>false]
]
]
);
}
public Customer (CustDetails cust) {
sys.assign(this, cust, CustDetails);
}
public static <text.sql.update int createTable () db=$testDb>
create table if not exists customer (
id integer not null auto_increment,
last_name varchar(16) not null,
dob date,
rest text,
primary key (id), index (last_name, dob)
)
</text.sql>
public void addAddress (Address addr) {
sys.append(addresses, addr);
}
}
</jag>
166
JavaGram Agile Development
For convenience, we’ve specified the customer fields in a separate class (CustDetails) so
that we can easily pass it to the Customer constructor.
Also, we’ve defined the table schema slightly differently this time (see the createTable()
method). The id column is now defined as ‘auto increment’, which means that it’s
controlled by the database server rather than the programmer – each time a new row is
added this value is incremented.
Note how Customer extends CustDetails and Object. The meta data required by Object is
specified in a static block by calling Object.defineTable(). This meta data is specified as
a map, which defines the database, the table, and the column mapping between Customer
and its table. We’ve used the same streamlined data modeling approach as introduced in
Section 5.6, by storing firstName, sex, and addresses fields in a single table column
(rest).
Here is a test method that exercises the persistence and searching functionality of
Customer as supported by Object.
static public void main () {
transaction ($testDb) {
dropTable(Customer, $testDb);
createTable();
}
Customer cust = new Customer([@CustDetails firstName=>"John", lastName=>"Smith",
dob=>[#1992-10-22], sex=>"Male"]);
cust.addAddress([@Address street=>"24 Mark St", town=>"Balwyn", state=>"Vic",
zip=>"3103"]);
cust.addAddress([@Address street=>"15 High St", town=>"Kew", state=>"Vic",
zip=>"3105"]);
cust.persist();
sys.println(Object.find(Customer));
cust.setFirstName("Peter");
cust.persist();
sys.println(Object.find(Customer, [$like=>[$lastName=>"Sm%"],
$exact=>[$dob=>[#1992-10-22]]]));
cust.setFirstName("John");
cust.refresh();
// Discard changes
sys.println(Object.findOne(Customer, $id, 1));
cust.delete();
sys.println(Object.findOne(Customer, $lastName, "Smith"));
}
The persist() method writes the object to the database according to the meta data rules.
The refresh() method does the opposite – it refreshes the object by reading it back from
the database, causing any unsaved changes made to the object since the last save to be
discarded.
SQL Programming
167
Object provides these search methods:

find(ClassName) performs a wildcard search, returning all instances of ClassName
stored in its underlying table, as a vector.

find(ClassName, criteria) returns all instances of ClassName that match the specified
criteria, as a vector. The latter is specified as a map that may contain a $like and/or
$exact submap, specifying field values that must match partially and/or exactly.

find(ClassName, field, value) returns all instances of ClassName whose field has
the specified value, or null if no match is found.

findOne(ClassName, field, value) returns the first matching instance of ClassName
whose field has the specified value, or null if no match is found.
The delete() method permanently deletes the object from the database.
5.7.2 File Handling
Sometimes, it makes sense to persist files to a database in the same way we persistent
other data. For example, in an administration system, customer requests (such as
application forms) may be scanned and the resulting images stored along with the
customer data, or in a call centre, recorded conversations (i.e., audio files) may be stored
with normal customer data. Storing these ‘attachments’ in a database is a far better
approach than keeping them on a file system, especially in a distributed system where
there may be many physical servers and a variety of file systems.
The Object class provides support for this type of file persistence. We’ll present a sample
class (called Document) that illustrates how you can easily implement this type of
functionality.
<jag domain="quest/bom">
<load>
"lib/lang/Common"
"lib/bom/Object"
"lib/gui/GuiApp"
"lib/gui/GuiUtil"
</load>
class User {
string username;
//...
setable static User currUser;
public User (string username) {
this.username = username;
}
}
class Document extends Object {
getable int id;
// Internal unique numeric document ID
getable string name;
// Document name
setable string path;
// Document path
168
JavaGram Agile Development
static {
defineTable(Document,
[ $db => $testDb
, $table => $document
, $columns =>
[ $id => $id
, $name => $name
, $content => [$fields=>$path, $viaFile=>$binary, $extend=>true]
]
]
);
}
public static <text.sql.update int createTable () db=$testDb>
create table if not exists document (
id integer not null auto_increment,
name varchar(64),
content mediumblob,
primary key (id)
)
</text.sql>
public Document (string name, string path) {
this.name = name;
this.path = path;
}
protected slocal string getViaFilePath (symbol dbColumn, symbol field, int rowNum) {
string dir @= sys.pathConc(sys.root, "temp/");
sys.createPath(dir);
return sys.pathConc(dir, "Doc" + rowNum + "_" + name);
}
public static Document findById (vague id) {
return Object.findOne(Document, $id, id)@Document;
}
public void save () {
if (path == null)
throw new Exception("no file path specified for " + name);
if (!sys.pathExists(path))
throw new Exception(path + " doesn't exist");
try {
// Save all fields, including file to 'content' column marked 'extend':
setExtendOn(true);
persist();
}
finally {
setExtendOn(false);
}
path = null;
}
public string retrieve () {
try {
// Get all fields, including file to 'content' column marked 'extend':
setExtendOn(true);
SQL Programming
169
refresh();
}
finally {
setExtendOn(false);
}
return path;
}
}
</jag>
The underlying document table has three fields: a primary id field, a name field, and a
content blob field for storing binary document data. The important point to note is how
the object schema specifies the content field in the static class initializer block:
$content => [$fields=>$path, $viaFile=>$binary, $extend=>true]
This specification states that content represents binary data sourced from a file, whose
path is denoted by the path field of Document. It also specifies that content is not retrieved
or stored unless the ‘extend’ feature of Object is switched on. The reason for the latter is
that often when you perform a query on this table (e.g., to get a list of all the documents
in the table), you don’t want the binary data to be also retrieved, as this data won’t be
needed and will carry a significant overhead. In some other cases (e.g., to view a
document), you want to retrieve this binary data. The Object.setExtendOn() method
allows you to exercise this control, and the Object.isExtendOn() method allows you to
get its current state (defaults to false).
Note how the save() and retrieve() methods of Document ensure that ‘extend’ is on
before they do their work and reset it back to off when they finish. This ensures that for
normal queries, such as Object.find(), ‘extend’ is always off and therefore not retrieving
any binary data.
Another important point to note is the getViaFilePath() method. When you store a
document in the database, its original path becomes irrelevant because we may be later
retrieving the file on another computer with a completely different file system. Therefore,
we don’t even store the file path – we just store the file name. The role of the path field of
Document is to denote the original file path at the time of persistence. During retrieval,
getViaFilePath() is internally called to determine the target path for the retrieved file.
You must override this method and ensure that it returns a sensible file path. The three
parameters of this method provide you with relevant information that you might want to
use to construct a unique path name. In our example, we’ve used the JavaGram root, the
document name, and the row number to construct a unique path. The method also needs
to ensure that the directory of this path exists. Otherwise, JavaGram won’t be able to
create the file later on.
Here is a simple GUI application that exercises Document.
170
JavaGram Agile Development
singleton class DocApp extends GuiApp {
static final vector<map> TABLE_FORMAT @= [
[$key=>$id, $title=>"ID", $width=>20, $align=>$east]
,[$key=>$name, $title=>"Name", $width=>100, $align=>$west]
,[$key=>$path, $title=>"Retrieved Path", $width=>300, $align=>$west]
];
<App app lookAndFeel=$windows>
<Frame frame title="Document App" width=400 height=200 event=frameHandler>
<Panel>
<Layout.border/>
<Panel lay=$north>
<Layout.flow align=$west/>
<Button title="Add File..." action={addFile()}/>
<Button title="View File" enable={table.select != null}
action={viewFile()}/>
<Button title="Remove File" enable={table.select != null}
action={removeFile()}/>
</Panel>
<Pane.scroll>
<Table table format={TABLE_FORMAT} autoSize=true event=tableHandler/>
</Pane.scroll>
</Panel>
</Frame>
</App>
<FileChooser fc owner={GuiUtil.getMainFrame()} kind=$open
title="Choose file(s)" path="C:/JavaGram/" label="Open"/>
public DocApp () {
super(frame);
populate();
}
protected void frameHandler (native comp, symbol event) {
if (event == $close)
exit();
}
protected vague tableHandler (native comp, symbol event) {
switch (event) {
case $select:
gui.maintain(frame);
break;
case $drill:
viewFile();
break;
}
return null;
}
protected void addFile () {
string path @= fc.show;
if (path != null) {
map<symbol, string> parts = sys.pathParts(path);
Document doc = new Document(parts[$name] + '.' + parts[$ext], path);
doc.save();
SQL Programming
171
populate();
}
}
protected void viewFile () {
Document doc @= table.data[table.select@int];
string path = doc.retrieve();
table.refresh = true;
Desktop.open(path);
}
protected void removeFile () {
Document doc @= table.data[table.select@int];
doc.delete();
populate();
}
protected void populate () {
vector<Document> vec @= Object.find(Document);
table.data = vec;
table.refresh = true;
}
public static void main () {
User.setCurrUser(new User("john"));
transaction ($testDb) {
//Object.dropTable(Document, $testDb);
Document.createTable();
}
DocApp.singleton.run();
}
}
When run, it displays the following frame (where we’ve already added two files).
You add a file by pressing the Add File button, browsing to the desired file, and pressing
Open. To view a file, select it in the table and press View File, or simply double-click its
row. When a file is retrieved, the Retrieved Path column shows the path into which it’s
been retrieved (as shown for ‘Test.doc’ in the above example). To remove a file, select its
row and press Remove File.
172
JavaGram Agile Development
The implementation of Document is intentionally minimal and intended to convey the
concept rather than serve as a model implementation. Other considerations that a more
complete implementation would need to deal with include:

Client-server deployment. In this case, the file typically originates from the clientside, and needs to be sent to the server-side first and then stored in the database. Upon
retrieval, the file needs to be sent back to the client side.

Compression. Before storing a file in the database, we may want to compress it to
minimize its size. After retrieving it, we would need to decompress it.

Locking. For editable files, a locking mechanism would be required so that multiple
users cannot concurrently modify the same file.
The Quest sample application described in Chapter 8 implements all the above for
attachments.
5.7.3 Locking
Persisted objects are often subject to concurrent access. Where an application retrieves an
object for read-only purposes, this poses no issue. However, where users are allowed to
modify objects, some form of locking is required. For example, suppose that in a CRM
application, a user retrieves a customer and attempts to update the details. If two users do
this concurrently for the same customer, the outcome would be potentially inconsistent,
as illustrated by the following scenario:

User A retrieves customer C.

User B retrieves customer C.

User A changes the customer’s title from ‘Mrs’ to ‘Ms’, and saves the change.

User B changes the customer’s marital status from ‘Married’ to ‘Divorced’ and saves
the change.

As a result of this sequence, the customer’s title remains incorrectly as ‘Mrs’
To avoid this, the application should lock the object as soon as the user attempts to
modify it. Furthermore, locking should cause the object to be refreshed from the
database, to ensure that the user is working on an up-to-date copy. Once the user has
saved the changes, the object should be unlocked, so that it becomes updatable by other
users. If a user attempts to update an object that’s currently locked by another user, the
application should detect and block the attempt.
SQL Programming
173
This scheme is implemented by the UserLockableObject library class, which in turn
extends Object. Therefore, if you need a locking mechanism for a BO, you should derive
it from this class instead of Object. Additionally, you should define a ‘lock’ column for
the underlying table. This should have a primitive type (e.g., integer, string, date) –
UserLockableObject treats this as a value of type vague.
To illustrate how this works, let’s redefine the Document class of the previous section so
that it supports locking. We’ll use a dummy User class to represent users.
<jag domain="quest/bom">
<load>
"lib/lang/Common"
"lib/bom/Object"
"lib/bom/UserLockableObject"
"lib/gui/GuiApp"
"lib/gui/GuiUtil"
</load>
class User {
getable string username;
//...
setable static User currUser;
public User (string username) {
this.username = username;
}
}
class Document extends UserLockableObject {
getable int id;
// Internal unique numeric document ID
getable string name;
// Document name
setable string path;
// Document path
static {
defineTable(Document,
[ $db => $testDb
, $table => $document
, $columns =>
[ $id => $id
, $name => $name
, $content => [$fields=>$path, $viaFile=>$binary, $extend=>true]
]
, $lock => [$column=>$locked_by, $style=>$byUser]
]
);
}
public static <text.sql.update int createTable () db=$testDb>
create table if not exists document (
id integer not null auto_increment,
name varchar(64),
content mediumblob,
locked_by varchar(16),
174
JavaGram Agile Development
primary key (id)
)
</text.sql>
public Document (string name, string path) {
this.name = name;
this.path = path;
}
protected slocal string getViaFilePath (symbol dbColumn, symbol field, int rowNum) {
string dir @= sys.pathConc(sys.root, "temp/");
sys.createPath(dir);
return sys.pathConc(dir, "Doc" + rowNum + "_" + name);
}
public static Document findById (vague id) {
return Object.findOne(Document, $id, id)@Document;
}
public void save () {
if (path == null)
throw new Exception("No file path specified for " + name);
if (!sys.pathExists(path))
throw new Exception(path + " doesn't exist");
string lockedBy @= lockedBy();
if (lockedBy != null && lockedBy != User.getCurrUser().getUsername())
throw new Exception("Document is locked by " + lockedBy);
try {
// Save fields, including file to 'content' column which is marked 'extend':
setExtendOn(true);
persist();
if (lockedBy != null)
unlock(lockedBy);
}
finally {
setExtendOn(false);
}
path = null;
}
public string retrieve () {
try {
// Get fields, including file to 'content' column which is marked 'extend':
setExtendOn(true);
refresh();
}
finally {
setExtendOn(false);
}
return path;
}
}
</jag>
Note how we’ve added the locked_by column to the document table, and specified it in the
object schema as being used for a ‘lock by user’ style of object locking:
SQL Programming
175
$lock => [$column=>$locked_by, $style=>$byUser]
The only other major changes are to the save() method, where we first check that the
object hasn’t been locked by another user and, after persisting the object to the database,
we release our lock on it, if any.
Let’s also extend our GUI test program to exercise the locking mechanism.
singleton class DocApp extends GuiApp {
static final vector<map> TABLE_FORMAT @= [
[$key=>$id, $title=>"ID", $width=>20, $align=>$east]
,[$key=>$name, $title=>"Name", $width=>100, $align=>$west]
,[$key=>$path, $title=>"Retrieved Path", $width=>300, $align=>$west]
];
static User user1 = new User("john");
static User user2 = new User("linda");
<App app lookAndFeel=$windows>
<Frame frame width=450 height=200 event=frameHandler>
<Panel>
<Layout.border/>
<Panel lay=$north>
<Layout.flow align=$west/>
<Button title="Add File..." action={addFile()}/>
<Button title="View File" enable={table.select != null}
action={viewFile(false)}/>
<Button title="Edit File" enable={table.select != null}
action={viewFile(true)}/>
<Button title="Save File" enable={table.select != null}
action={saveFile()}/>
<Button title="Remove File" enable={table.select != null}
action={removeFile()}/>
<Button title="Change User" action={changeUser()}/>
</Panel>
<Pane.scroll>
<Table table format={TABLE_FORMAT} autoSize=true event=tableHandler/>
</Pane.scroll>
</Panel>
</Frame>
</App>
<FileChooser fc owner={GuiUtil.getMainFrame()} kind=$open
title="Choose file(s)" path="C:/JavaGram/" label="Open"/>
public DocApp () {
super(frame);
changeUser();
populate();
}
protected void frameHandler (native comp, symbol event) {
if (event == $close)
exit();
176
JavaGram Agile Development
}
protected vague tableHandler (native comp, symbol event) {
switch (event) {
case $select:
gui.maintain(frame);
break;
case $drill:
viewFile(false);
break;
}
return null;
}
protected void addFile () {
string path @= fc.show;
if (path != null) {
map<symbol, string> parts = sys.pathParts(path);
Document doc = new Document(parts[$name] + '.' + parts[$ext], path);
doc.save();
populate();
}
}
protected void viewFile (boolean edit) {
Document doc @= table.data[table.select@int];
string path = doc.retrieve();
table.refresh = true;
if (edit) {
string lockedBy @= doc.lockedBy(false);
if (lockedBy == null) {
doc.lock(User.getCurrUser().getUsername());
} else if (lockedBy != User.getCurrUser().getUsername()) {
gui.alert("Can't Edit", "File locked by " + lockedBy,
GuiUtil.getMainFrame(), $error);
return;
}
}
Desktop.open(path);
}
protected void saveFile () {
Document doc @= table.data[table.select@int];
try {
doc.save();
}
catch (Exception e) {
gui.alert("Can't Save", e.getMessage(), GuiUtil.getMainFrame(), $error);
}
}
protected void removeFile () {
Document doc @= table.data[table.select@int];
doc.delete();
populate();
}
SQL Programming
177
protected void changeUser () {
User.setCurrUser(User.getCurrUser() == user1 ? user2 : user1);
frame.title = "Doc App - User: " + User.getCurrUser().getUsername();
}
protected void populate () {
vector<Document> vec @= Object.find(Document);
table.data = vec;
table.refresh = true;
}
public static void main () {
transaction ($testDb) {
//Object.dropTable(Document, $testDb);
Document.createTable();
}
DocApp.singleton.run();
}
}
The new user interface looks like this:
The Edit File button calls viewFile(true) which attempts to obtain a lock on the object if
it’s not already locked. Conversely, the Save File button calls the save() method of the
object, which persists it and releases the lock only if the object is locked by the current
user.
To properly test all conditions, we’ve also added a Change User button which toggles the
current user between two pre-created users (John and Linda). When you first run the
application, the current user is set to John. At this point, edit a file, but don’t press the
Save File button, so that the object remains locked. Now press the Change User button to
switch the current user to Linda. Now attempt to edit the file again – you’ll notice that
this will be blocked and the application will complain that the object is locked by John.
You can test other conditions in a similar manner to verify that locking behaves as
expected.
178
JavaGram Agile Development
6 Advanced Topics
This chapter describes a number of advanced programming concepts, some of which are
unique to JavaGram. These concepts are specifically aimed at the development of 3-tier
distributed applications. The tiers being: data tier (embodied by data sources, such as
database servers), business functionality tier (embodied by application servers), and
presentation tier (embodied by clients, whose code are dynamically delivered by
application servers). The actual deployment options and models for distributed
applications are discussed in the next chapter. This chapter focuses on the underlying
programming techniques that make such deployments possible.
6.1 Client-Server Communication
The examples we’ve seen so far in this book deal with monolithic applications, where the
application runs as a single process. We call such applications standalone because they
can run on a single machine. Standalone applications can support a limited client-server
model, where the application is deployed on a number of machines, each of which
interacts via SQL with a database server running on yet another machine. This is called
a 2-tier client-server model, because of the physical separation of the database server and
the application – the latter is often called a fat client. This model offers limited
scalability because each fat client instance is limited by the resources available on its host
machine.
The 3-tier model introduces a middle-tier – called an application server – that hosts the
bulk of the business logic, thus reducing the load on each client instance. The JavaGram
code residing in the middle tier contains both the business logic (which will run in the
middle tier) and the presentation code (which will be transferred to and run in each
client). The application server dynamically compiles these two types of binaries from a
common source code – hence the term hybrid client. The client instances communicate
with the application server via JavaGram Transfer Protocol (JTP), which is somewhat
similar to HTTP.
Advanced Topics
179
The important point to note here is that there is one overall source code for the
application, and this code is released only to the middle tier. JavaGram manages the rest.
This is fundamentally different from other client-server programming paradigms, where
the middle tier and the client are coded separately, which in turn creates the additional
overhead of keeping the two in sync for the rest of the program’s operational lifetime.
JavaGram completely hides the JTP client-server communication details from the
programmer. The programmer works in terms of a much simpler conceptual model that
deals with remoting – the ability to distinguish between what runs locally and what runs
remotely – in a declarative style.
6.1.1 Remote Methods
When you define a method in a class, you can use the remote qualifier to specify that it
should run on a server. The significance of this qualifier depends directly on how the
program is deployed. There are three possibilities:

If the program is deployed to run as standalone, then this qualifier has no
significance.

If the program is deployed as client-server and the method’s class is sourced locally
then the qualifier has no significance.

If the program is deployed as client-server and the method’s class is sourced from a
server then the qualifier ensures that the method will execute on that server, not
locally.
To avoid confusion, note that ‘client’ and ‘server’ are relative terms. For example,
consider the following diagram.
Suppose that Client obtains its code from Server A, which in turn obtains its code from
Server B. In this scenario, Server A is a server for Client, but a client of Server B.
Therefore, Server A is both a server and a client.
As a general rule, if a method foo() is sourced from a server, it will run on that server
regardless of whether it’s called from that server or elsewhere.
Recall the Person class from the previous chapter. We’ll rewrite this class with the
intention of running it in client-server mode.
180
JavaGram Agile Development
<jag domain="doc/code/chap6">
class Person {
protected getable int id;
protected setable string firstName;
protected setable string lastName;
protected setable string sex;
protected setable date dob;
protected setable boolean married;
public remote void insert () {
transaction ($testDb) {
_insert();
native rs = _uniqueId();
id @= sql.next(rs) ? sql.get(rs, $id) : 0;
}
}
public remote void update () {
transaction ($testDb) {
_update();
}
}
public remote void delete () {
transaction ($testDb) {
_delete(id);
id = null;
}
}
public remote void refresh () {
sys.assign(this, find(id), Person);
}
public remote static Person find (int id) {
query ($testDb) {
native rs = _find(id);
return sql.next(rs) ? sql.getRow@Person(rs) : null;
}
}
public remote static vector<Person> find () {
query ($testDb) {
native rs = _find();
vector<Person> vec @= vector();
while (sql.next(rs))
sys.append(vec, sql.getRow@Person(rs));
return vec;
}
}
public remote static void resetTable () {
// Because this is a test, we drop the tables and re-create them:
transaction ($testDb) {
Person.dropTable();
Person.createTable();
Advanced Topics
181
}
}
protected <text.sql.update int _insert () db=$testDb>
insert into person (first_name, last_name, sex, dob, married)
values ({`firstName}, {`lastName}, {`sex}, {`sys.format(dob)}, {`married})
</text.sql.update>
protected <text.sql.update int _update () db=$testDb>
update person
set first_name = {`firstName},
last_name = {`lastName},
sex = {`sex},
dob = {`sys.format(dob)},
married = {`married}
where id = {`id}
</text.sql.update>
protected static <text.sql.update int _delete (int id) db=$testDb>
delete from person
where id = {`id}
</text.sql.update>
protected static <text.sql.query native _uniqueId () db=$testDb>
select id from person where id is null
</text.sql.query>
protected static <text.sql.query native _find (int id) db=$testDb>
select * from person where id = {`id}
</text.sql.query>
protected static <text.sql.query native _find () db=$testDb>
select * from person
</text.sql.query>
static <text.sql.update native createTable() db=$testDb>
create table if not exists person(
id integer not null auto_increment,
first_name varchar(16),
last_name varchar(16),
sex varchar(8),
dob date,
married bit,
primary key(id), index(last_name))
</text.sql.update>
static <text.sql.update native dropTable() db=$testDb>
drop table if exists person
</text.sql.update>
}
</jag>
We’ve defined the table schema slightly differently this time (see the createTable()
method). The id column is now defined as ‘auto increment’, which means that it’s
controlled by the database server rather than the programmer – each time a new row is
added this value is incremented.
The class contains these remote methods:
182
JavaGram Agile Development

insert() calls _insert() to perform the SQL insertion and then calls _uniqueId() to
obtain the ID allocated by the database, to update the object’s id.

update() calls _update() to perform the SQL to store the current state of the object in
the database.

delete() calls _delete() to issue the SQL for deleting the object from the database.

refresh() refreshes the object from the database by finding it and using sys.assign()
to copy its fields to the object.
The class also contains these static remote methods:

find(id) finds a Person using its unique ID.

find() finds all instances of Person in the database.

resetTable() deletes and recreates the person table for testing purposes.
Here is a test program for exercising this class.
singleton class People {
public static void populatePerson () {
vector<Person> persons = [
[@Person firstName=>"Linda", lastName=>"Black", sex=>"Female",
dob=>[#1980-12-20], married=>true]
,[@Person firstName=>"Mark", lastName=>"Adams", sex=>"Male",
dob=>[#1988-02-13], married=>false]
];
for (Person p in persons)
p.insert();
}
public static void main () {
Person.resetTable();
populatePerson();
Person p = Person.find(1);
sys.println(p);
p.setMarried(false);
p.update();
sys.println(p);
p.setFirstName("Belinda");
p.refresh();
// Discard changes
sys.println(p);
p.delete();
sys.println(p);
p = Person.find(1);
sys.println(p);
}
}
The program produces the same output whether run in standalone or client-server mode:
Advanced Topics
183
[@doc\code\chap6\Person dob=>[#1980-12-20], firstName=>"Linda",
lastName=>"Black", married=>true, sex=>"Female"]
[@doc\code\chap6\Person dob=>[#1980-12-20], firstName=>"Linda",
lastName=>"Black", married=>false, sex=>"Female"]
[@doc\code\chap6\Person dob=>[#1980-12-20], firstName=>"Linda",
lastName=>"Black", married=>false, sex=>"Female"]
[@doc\code\chap6\Person dob=>[#1980-12-20], firstName=>"Linda",
lastName=>"Black", married=>false, sex=>"Female"]
Null
id=>1,
id=>1,
id=>1,
id=>null,
To run the program in client-server mode, you need to first define a configuration file for
the server. Here is a minimal server configuration file.
[ $download=>[ $default=>["jag"=>$private, "*"=>$public]
, $cache=>"mycache/"
]
, $database=>[ $testDb=>[ $name=>"Test Database"
, $url=>"jdbc:mysql://localhost/test"
, $user=>"root"
, $password=>"password"
, $timeout=>[$login=>30, $query=>20]
, $driver=>"com.mysql.jdbc.Driver"
]
]
]
The $database part is as explained in the previous chapter. The $download part specifies
two things:

The $default download rule states that source files (.jag) are private and therefore
not downloadable, but everything else (including compiled .jax files) is public and
therefore downloadable.

The $cache key nominates the server’s cache directory. This is where the server
places compiled files for client and server ends.
The standard method of running an application server is to use the lib/svr/AppServer.jag
library script. For example:
java -cp "C:/JavaGram/jar/JAG.jar;C:/JavaGram/src/jar/mysql-connector.jar"
jag.run.svr.Server -root "C:/JavaGram/src" -config "doc/code/chap6/Config.jag" host "localhost:443" "lib/svr/AppServer.jag"
Note how we’ve specified the server’s URL using the –host option. This states that the
server will run on the localhost and listen to client requests on port 433. When you run
this server, you should initially get the following output.
[2009-08-07 11:19:16] <main> Root set to: C:/JavaGram/src
[2009-08-07 11:19:16] <main> Config file set to: C:/JavaGram/src/doc/code/chap6/Config.jag
184
JavaGram Agile Development
=============== APP SERVER STARTING ===============
[2009-08-07 11:19:16] <main> Synchronous server started on localhost:443
[2009-08-07 11:19:20] <Session-1> Session started: id=1, alive=1, client=/127.0.0.1:1804
[2009-08-07 11:19:20] <Session-1> Session closed: id=1, alive=0, client=/127.0.0.1:1804
The server is now ready to receive client connections.
You boot a client against a server by nominating the server’s URL and the initial script to
be executed. For example:
java -cp "C:/JavaGram/jar/JAG.jar" jag.run.Env -root "C:/JagClient" -host "localhost:443"
-stage prod -boot "doc/code/chap6/Person"
The initial script is specified using the –boot option. Note that we don’t need to specify
the file’s extension. The client requests the Person script from the server. If this is the first
time the server has received such a request, it will locate and compile Person.jag to
produce two Person.jax compiled versions: one for client’s use (from which all remote
method implementations have been stripped) and one for the server’s use (from which all
GUI implementations have been stripped). The former is sent to the client which the
client then loads and executes.
Now consider the call Person.find(1). Because this is a remote call, it’s not executed on
the client-side. The client sends a request to the server to perform this call. In response,
the server loads its own version of Person.jax and then performs the call. The return
value is a Person object which the server serializes and sends back to the client. The client
converts this serialized data into a Person object which it then uses as the return value of
the call.
Now consider the call p.update(). This is also a remote call, but to a non-static method.
The protocol is as before, plus two additional steps:

The object p is serialized and sent to the server along with the call. The server
converts this serialized data into a Person object and makes the call on that object.

When the call returns, the server serializes the object and returns it back to the client.
The client converts this serialized data into a Person object and uses it to update the
original object.
In other words, JavaGram manages calls to remote methods (be they static or non-static)
such that the semantics of the call are honored, regardless of the deployment model. This
level of consistency is essential in ensuring that the programmer is not overburdened by
different rules for different circumstances.
6.1.2 Remote Classes
As mentioned earlier, non-static remote calls involve the transportation of the object
between the client and the server ends. Sometimes this is not desirable and we prefer to
Advanced Topics
185
keep an object permanently on the server side whilst still wanting to make its methods
accessible to the client. Reasons may include:

The large size of the object might make it unsuitable for timely transportation.

There may be security concerns around the private information held within the object.

The object may have resource dependencies that tie it to the server end.

The object may be a singleton.
Remote classes provide an elegant solution for such scenarios. Let’s look at an example.
Imagine an online chat-room where individuals can jump in freely and take part in textbased conversations. First, we need a class to represent a chat room member.
class Member {
getable int id;
getable string name;
getable date joined = sys.date();
setable int refreshLen = 0;
static int lastId = 0;
public Member (string name) {
id = ++lastId;
this.name = name;
}
public string toString () {
return name + "#" + id;
}
}
Each member has a name and is allocated a unique numeric ID, and a record of their
joining time. The chat room itself is defined as a remote class.
remote class ChatRoom {
string name;
map<int, Member> people @= map();
vector<string> chat @= vector();
static map<string, ChatRoom> rooms @= map();
//
//
//
//
Room name
Member ID => Member
Everything spoken so far
All the chatrooms
public ChatRoom (string name) {
this.name = name;
}
public void release () {
sys.println("ChatRoom.release() called");
sys.remove(rooms, name);
}
public int join (string name) {
Member mem = new Member(name);
people[mem.getId()] = mem;
say(mem.getId(), $"<joined {this.name}>");
186
JavaGram Agile Development
return mem.getId();
}
public void leave (int id) {
say(id, $"<left {name}>");
sys.remove(people, id);
}
public void say (int id, string msg) {
Member mem = people[id];
string ts = sys.format(sys.date(), "dd MMM, HH:mm:ss");
sys.append(chat, $"[{ts} {mem.toString()}]: {msg}");
}
public vector<string> refresh (int id) {
Member mem = people[id];
int len = sys.length(chat);
if (mem.getRefreshLen() >= len)
return null;
vector<string> vec @= vector($"Refresh for {mem.getName()}:");
for (int i = mem.getRefreshLen(); i < len; ++i)
sys.append(vec, chat[i]);
mem.setRefreshLen(len);
return vec;
}
public static ChatRoom getRoom (string name) {
ChatRoom room = rooms[name];
if (room == null)
room = rooms[name] = new ChatRoom(name);
return room;
}
}
Note that this class can potentially contain a lot of data – it keeps track of everyone who
joins as well as a complete record of all conversations. If we were to allow an instance of
this class to move between client and server ends, it would create a performance
overhead that would deteriorate over time.
Every method of a remote class is, by definition, remote. The join() method allows a
person to join a chat room as a member. Conversely, leave() removes the member. The
say() method is invoked when the individual wants to say something. This is
timestamped and simply added to the chat trail.
As a chat progresses, each individual would want to see what’s been said by others. The
refresh() method does this optimally. It keeps track of the last refresh point for the
member and returns everything said after that point.
Finally, a chat room is initially accessed by calling getRoom(), which returns the room if it
exists, or creates a new one. An important point to bear in mind is that a remote class
cannot be instantiated on the client side because such instances can only reside on the
server side. Therefore, you always need a means of getting a remote class instance from
Advanced Topics
187
the server itself. This can be either a static method in the remote class itself (as we’ve
done her) or a remote method of another class.
Here is a sample program for exercising our remote class.
class TestChatRoom {
static ChatRoom room = ChatRoom.getRoom("blue");
public static void showRefresh (int id) {
for (string str in room.refresh(id))
sys.println(str);
}
public static void main () {
int john = room.join("john");
int anne = room.join("anne");
room.say(john, "What a beautiful day");
int sarah = room.join("sarah");
room.say(anne, "Hmmm, not here - it's hailing!");
showRefresh(anne);
room.say(sarah, "Can we stop chatting about the weather?");
room.say(anne, "OK, let's talk politics");
showRefresh(john);
room.leave(sarah);
showRefresh(anne);
sys.release(room);
}
}
It produces the following output.
Refresh for anne:
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
Refresh for john:
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:55
Refresh for anne:
[07 Aug, 16:29:55
[07 Aug, 16:29:55
[07 Aug, 16:29:56
john#1]: <joined blue>
anne#2]: <joined blue>
john#1]: What a beautiful day
sarah#3]: <joined blue>
anne#2]: Hmmm, not here - it's hailing!
john#1]: <joined blue>
anne#2]: <joined blue>
john#1]: What a beautiful day
sarah#3]: <joined blue>
anne#2]: Hmmm, not here - it's hailing!
sarah#3]: Can we stop chatting about the weather?
anne#2]: OK, let's talk politics
sarah#3]: Can we stop chatting about the weather?
anne#2]: OK, let's talk politics
sarah#3]: <left blue>
Internally, JavaGram manages remote objects according to these rules:
188
JavaGram Agile Development

A remote object is allocated a unique ID.

When a client obtains a reference to a remote object, it gets a proxy object instead.

Any operation applied to the proxy object on the client side is communicated to the
server side and applied to the remote object instead.

Remote objects are reference-counted on the server side. When the reference count
reaches zero, the server automatically calls the release() method of the remote class
(if defined). This gives the class the opportunity to release any resources tied up by
the object. At the same time, the object is permanently removed from the server
cache.
JavaGram keeps track of the remote objects accessed by a client (within the client’s
server-side session). When the client exits, the reference counts of these objects are
automatically decremented. Additionally, a client can call sys.release() on a remote
object that it no longer needs, causing the object’s reference count to be decremented on
the server side. Once the client releases a proxy object, it can no longer use it.
Remote objects can be shared by server-side client sessions, so it’s possible for multiple
client sessions to concurrently access the same remote object. In such cases, you should
use synchronization (described later in the chapter) to avoid concurrency issues.
6.1.3 Exception Handling
When working with remote methods or classes, the normal exception handling rules
apply. For example, if you call a remote method and it fails on the server-side, the
exception will be automatically transferred to the client-side and delivered to the caller.
In other words, the programmer doesn’t need to take any special steps to handle potential
exceptions for remote calls.
If a client process is terminated abnormally, JavaGram will detect this and terminate the
corresponding server-side session. This prevents server-side resources from being
unnecessarily tied up. Conversely, if a server-side session terminates abnormally, the
client will detect this and prompt the user.
6.1.4 Targeted Remote Calls
A client can connect to multiple servers. Of these, one is the default server – the one that
the client was originally booted against – whose connection is always denoted by
sys.loader. As a general rule, when you make a remote call, JavaGram will send the call
to the server from which the method’s class was originally sourced – we call this the
class’s source server which, in most cases, is the same as the default server.
There may be legitimate cases where you want to source your code from one server and
use that code to issue calls against other servers. You can do this using a targeted remote
call. This is just like a normal remote call, but explicitly specifies the target server using
this syntax:
Advanced Topics
189
TargetServerStream::MethodName (Arguments)
For example, suppose that you have a distributed application for recording and reporting
summary sales results of a company. For ease of understanding, we’ve represented this as
a very simple class below. The getSummary() remote method fakes its data so that we can
easily get different data on different servers. To try this example, you need to run three
servers, all running on localhost using ports 443, 444, and 446, and a client against
localhost:443.
<jag domain="doc/code/chap6">
class Summary {
string lob;
// Line of business
date from;
// Start date
date to;
// End date
real inflows;
real outflows;
public static remote Summary getSummary (date from, date to) {
string lob = sys.subStr(sys.host, 5); // Fake a LOB name
int n = sys.date()@int;
real inflows = (n % 80) * 400;
// Fake a figure
real outflows = (n % 30) * 300;
// Fake a figure
return object(Summary, lob=>lob, from=>from, to=>to, inflows=>inflows,
outflows=>outflows);
}
public static void main () {
vector<stream> hosts = vector(
sys.loader
,sys.client("localhost", 444)
,sys.client("localhost", 446)
);
date from = [#2009-8-1];
date to = [#2009-8-31];
sys.println("Sales summary for ", sys.format(from), " to ", sys.format(to));
sys.println("LOB\t\t\tINFLOW \tOUTFLOW\tNET");
real inTotal = 0.0;
real outTotal = 0.0;
for (stream host in hosts) {
Summary s = host::getSummary(from, to);
inTotal += s.inflows;
outTotal += s.outflows;
sys.println(s.lob, '\t', s.inflows, '\t', s.outflows, '\t',
s.inflows - s.outflows);
}
sys.println("\t\t\t-------\t-------\t-------");
sys.println("TOTAL:\t\t", inTotal, '\t', outTotal, '\t', inTotal - outTotal);
for (stream s in hosts) {
if (s != sys.loader)
sys.close(s);
}
190
JavaGram Agile Development
}
}
</jag>
In the main() method, we create explicit client connections to localhost:444 and
localhost:446 using sys.client(). These two connections plus the default connection
give us three servers to issue requests against. We then loop through these and invoke
getSummary() against each server, and output the result returned by each server, which
looks something like this:
LOB
host:443
host:444
host:446
TOTAL:
INFLOW
29200.0
3600.0
9600.0
------42400.0
OUTFLOW
3900.0
8700.0
4200.0
------16800.0
NET
25300.0
-5100.0
5400.0
------25600.0
Finally, we close the explicit server connections by calling sys.close().
6.1.5 Clocal, slocal, and side
We’ve already seen how private, protected, and public qualifiers can be used to specify
the visibility of a class’s members with respect to other classes. There are two more
qualifiers that control visibility, but with respect to the client-server boundary:

The clocal qualifier can be applied to a class or its methods to make them local to a
client, thus making them inaccessible to servers.

Conversely, the slocal qualifier can be applied to a class or its methods to make them
local to a server, thus making them inaccessible to clients.
These two qualifiers do not alter the semantics of a class or its members, but provide
useful protection against inadvertent access outside their intended environment.
You can also limit an entire script’s visibility to the client or server side by setting its
side property in the <jag> element to $client or $server (defaults to $both).
Obviously, clocal and slocal are only meaningful for scripts that are available on both
sides. If such a script, for instance, has methods that refer to GUI elements, it would be
sensible to define them as clocal. On the other hand, methods that are intended to
perform SQL on the server side would be obvious candidates for slocal qualification.
For example, the Account class (defined in Chapter 5) would be better defined as:
class Account {
string accNo;
string accType;
int owner;
// ID of Person who owns the account
slocal static <text.sql.update native createTable() db=$testDb>
Advanced Topics
191
create table if not exists account(
acc_no varchar(16) not null,
acc_type varchar(16),
owner integer not null,
primary key(acc_no), index(owner))
</text.sql.update>
slocal static <text.sql.update native dropTable() db=$testDb>
drop table if exists account
</text.sql.update>
slocal <text.sql.update int insert () db=$testDb>
insert into account (acc_no, acc_type, owner)
values ({`accNo}, {`accType}, {`owner})
</text.sql.update>
}
6.2 Threads
A JavaGram thread (a thread of execution within a program) is a wrapping of its
underlying Java thread and follows similar implementation rules. Multiple threads can
execute concurrently within the same process, each having its own stack, but all sharing
the same static data. A running program contains a main thread and potentially other
threads for managing ancillary activities, such as garbage collection and GUI event
dispatching. An application can create additional threads for handling application specific
tasks, such as bulk reporting.
6.2.1 Working with Threads
To create a thread, subclass the Thread library class, defined in lib/lang/Common.jag. This
class has an abstract run() method that you need to implement.
abstract class Thread {
public Thread (string name, boolean daemon = false) {
//...
}
abstract protected vague run ();
//...
}
As suggested by the constructor, all threads are named, and can be either defined as
daemon or user thread. The Java Virtual Machine continues running until all remaining
threads are daemon threads.
Here is a simple program that demonstrates the concurrent behavior of threads. It creates
two threads and starts them. The run() method iterates three times, each time outputting a
message about the thread, and finally returns the thread name as a symbol.
<jag domain="doc/code/chap6">
<load>
"lib/lang/Common"
192
JavaGram Agile Development
</load>
class MyThread extends Thread {
public MyThread (string name) {
super(name, false);
}
protected vague run () {
for (int i = 1; i <= 3; ++i)
sys.println("In thread: " + getName());
return sys.strSym(getName());
}
public static void main () {
MyThread mt1 = new MyThread("first");
MyThread mt2 = new MyThread("second");
mt1.start();
mt2.start();
mt1.join(500);
mt2.join(500);
sys.println($"mt1 result = {mt1.getResult()}");
sys.println($"mt2 result = {mt2.getResult()}");
}
}
</jag>
A thread is started by calling its start() method. Once the run() method of the thread
returns, the thread dies. The join() method waits for up to a given length of time
(specified in milliseconds) until the thread dies. The getResult() method returns the final
result returned by the thread’s run() method, or null if it’s still running.
The output of this program will look something like this:
In thread:
In thread:
In thread:
In thread:
In thread:
In thread:
mt1 result
mt2 result
second
first
first
second
first
second
= $first
= $second
You can interrupt a running thread by calling its interrupt() method. To find out if a
thread is still running, call its isAlive() method. Within the run() method, if you want to
give other threads execution priority, call the yield() method.
It’s important to note that once a thread dies, it can’t be reused. If you want to rerun the
thread, you must create another instance of the class and call start() on it.
Within a JavaGram application server, each client session is represented by a thread. The
run() method of this thread is an infinite loop that receives client requests and responds
to them, exiting only when the client terminates.
Advanced Topics
193
6.2.2 Synchronization
Because threads can concurrently access static data, care needs to be taken to avoid
unwanted concurrency issues. For example, if multiple threads modify a static vector,
you need to make sure that only one thread at a time is allowed to do so. Otherwise, the
result will be unpredictable or may even cause runtime errors.
You can protect a section of code (called critical section) from concurrent access using
the synchronized statement:
synchronized (obj) {
//...critical section...
}
You can use any JavaGram value for obj. When a thread attempts to execute this
statement, it first requests a lock on obj. This lock is granted only if no other thread is
executing the critical section. Once the successful thread has executed the critical section,
this lock is released so that other threads can go through the same process.
Alternatively, you can make the body of an entire non-static method synchronized using
the synchronized qualifier:
synchronized void foo () {
//...
}
This is equivalent to writing:
void foo () {
synchronized (this) {
//...
}
}
Note that in both cases, this is used for locking purposes, which is effective only if the
threads operate on the same object.
Here is a modified version of our earlier example, where two threads use synchronization
to append data to a static vector.
class MyThread extends Thread {
static vector vec = vector();
//...
protected vague run () {
for (int i = 1; i <= 3; ++i) {
synchronized (vec) {
sys.append(vec, getName());
194
JavaGram Agile Development
}
}
return sys.strSym(getName());
}
}
When using synchronization, you must be careful not to introduce a deadlock situation.
A deadlock occurs when two threads are waiting for each other to release locks so that
the other can continue, causing the program to hang indefinitely. Here is a contrived
example to illustrate the point.
void foo1 () {
synchronized (one) {
if (!b)
foo2();
}
}
void foo2 () {
synchronized (two) {
if (b)
foo1();
}
}
Suppose that in thread A, b is false and a call is made to foo1(). Also suppose that in
thread B, b is true and a call is made to foo2(). Here is what may happen next:

Thread A gets a lock on one.

Thread B gets a lock on two.

Thread A executes foo2() inside the if statement.

Thread B executes foo1() inside the if statement.

Thread A is blocked on two, because B has a lock on it.

Thread B is blocked on one, because A has a lock on it.
The only practical remedy to a deadlock is to recode it such that the deadlock situation is
avoided.
6.2.3 ThreadLocal Fields
Sometimes it’s desirable to have a static field whose value is local to each thread. In other
words, you want the value to behave statically within each thread, but you don’t want it
shared across threads. The ThreadLocal class defined in lib/lang/Common.jag supports
this behavior.
To illustrate its effect, let’s use it in our thread example.
Advanced Topics
195
class MyThread extends Thread {
static vector vec = vector();
static ThreadLocal tl = new ThreadLocal(null);
//...
protected vague run () {
tl.set(getName());
for (int i = 1; i <= 3; ++i) {
synchronized (vec) {
sys.append(vec, getName());
sys.println(tl.get());
}
}
return sys.strSym(getName());
}
Here, we’re setting the tl value before the loop and outputting it inside the loop. The
program output will look something like this:
first
second
second
second
first
first
mt1 result = $first
mt2 result = $second
vec = ["first", "second", "second", "second", "first", "first"]
As demonstrated by the output, each thread is keeping a separate value for tl, even
though it’s static.
6.2.4 Timer Class
We saw in Chapter 4 how the <Timer> GUI element can be used to manage periodic tasks
that access GUI components. There is also a Timer class defined in lib/lang/Common.jag
that can be used for non-GUI purposes. This is often used on the server side to manage
periodic tasks. Internally, both <Timer> and Timer use a separate thread that sleeps until
the timer fires.
Timer has an associated class called TimerTask. To define a periodic task, you subclass
TimerTask and override its run() method. The task can then be scheduled using the timer.
The same timer can be used to schedule any number of tasks.
As an example of how to use Timer, suppose that you have a table where draft
transactions are written, with the expectation that another thread will periodically poll
this table, perform the recent draft transactions, and remove them from the table. Given
that none of this involves a GUI, we can implement the scheme using a Timer.
196
JavaGram Agile Development
<jag domain="doc/code/chap6">
<load>
"lib/lang/Common"
</load>
class PollTask extends TimerTask {
public PollTask (string name) {
super(name);
}
protected vague run () {
sys.println("Running task: " + getName());
// TODO: poll a transaction table and act on it.
return null;
}
public static void main () {
Timer timer = new Timer("polling timer");
PollTask task = new PollTask("txn polling");
timer.schedule(task, sys.date(), 1000);
sys.sleep(2000);
sys.println(sys.date(task.scheduledExecTime()));
timer.cancel();
sys.println("Done");
}
}
</jag>
To keep this example simple, we’ve excluded the internal implementation of run() for
polling the transaction table, and instead just output a message. The main() method
creates an instance of Timer and an instance of PollTask, and register’s the latter by
invoking the schedule() method of the former. The second argument of schedule()
specifies the start time of the scheduled task (or an initial delay in milliseconds), and the
final argument specifies the timer’s interval expressed in milliseconds for running the
task. So, here, we’ve specified the task to start immediately and then repeat every second.
You can find out the next scheduled time for a task’s execution by calling its
scheduledExecTime(). Finally, you can cancel a task or the timer itself by calling its
cancel() method.
This program will produce the following output.
Running task: txn polling
Running task: txn polling
Running task: txn polling
2009-08-12 15:07:59.453000000
Done
6.3 Asynchronous Behavior
By default, a method call (be it local or remote) is handled synchronously – the calling
thread blocks until the called method returns. JavaGram also allows you to call a method
asynchronously. In this case, the calling thread continues its execution immediately after
Advanced Topics
197
the async call and does not wait for the method to return. When the method finally
returns, a callback is invoked to do post processing.
The typical format of an async call is
MethodCall -> Callback -> ErrCallback
where ErrCallback is optional and, if present, is invoked instead of Callback when the
async call fails. Otherwise, Callback is deemed to be also responsible for exception
handling. A callback can be any valid expression, statement, or even a block.
Async calls are useful when you don’t want a thread to block while a potentially lengthy
task is executing. You can achieve the same effect by creating your own thread and
managing the call in that thread, but the async call syntax is an elegant alternative that
can save you unnecessary coding.
Recall how the <Worker> element described in Chapter 4 executes on the Swing’s event
dispatcher thread, so that it can safety update the GUI. Because async callbacks can
potentially update the GUI, they also execute on the Swing’s event dispatcher thread.
6.3.1 Local Asynchronous Call
Here is an example to illustrate the use of a local async call.
<jag domain="doc/code/chap6">
class AsyncTest {
static vector<map> data = [
[$name=>"John", $age=>20, $sex=>"Male"]
,[$name=>"Linda", $age=>22, $sex=>"Female"]
,[$name=>"Albert", $age=>18, $sex=>"Male"]
,[$name=>"Fiona", $age=>27, $sex=>"Female"]
];
public remote vector search (map criteria) {
vector result = vector();
for (map m in data) {
//10 / 0;
// Intentionally cause divide by zero error
boolean match = true;
for (vague key in criteria) {
if (m[key] != criteria[key]) {
match = false;
break;
}
}
if (match)
sys.append(result, m);
}
return result;
}
public void show (vector result) {
198
JavaGram Agile Development
sys.println(result);
}
public void error () {
sys.println("error() called");
}
public static void main () {
AsyncTest test = new AsyncTest();
vector res = test.search([$sex=>"Male"]) -> test.show(res) -> test.error();
sys.println("After async call");
}
}
</jag>
Within the main() method, search() is called asynchronously. Note that the return value
of this call (res) is accessible to the callback method. If you run this program, it will
produce the following output.
After async call
[[$age=>20, $name=>"John", $sex=>"Male"], [$age=>18, $name=>"Albert",
$sex=>"Male"]]
This clearly shows that the callback is invoked after execution has progressed beyond the
async call. Now, if you uncomment the division by zero line in search() to intentionally
cause an error, you will get the following output instead.
After async call
error() called
JavaGram handles a local async call by creating a separate thread and handing over the
management of the call to that thread. However, because of its single-threaded nature, the
Flash version of JavaGram runtime treats a local async call synchronously.
6.3.2 Parallel Processing
Async calls make it very easy to implement parallel processing algorithms that
significantly increase the runtime speed of your programs, especially on multi-CPU
computers.
As a practical example, suppose that your program needs to convert a bunch of image
files from one format to another. If the conversion process is computationally intensive,
you can speed things up by processing the images in parallel.
<jag domain="doc/code/chap6">
class AsyncTest {
vector<string> bitmapsToGifs (vector<string> bitmapImages) {
vector<string> gifImages @= vector();
for (string bitmap in bitmapImages) {
string gif = bitmapToGif(bitmap) -> addGif(gifImages, gif);
}
Advanced Topics
199
// Wait until all images have been converted:
do {
sys.sleep(10);
} while (sys.length(gifImages) != sys.length(bitmapImages));
return gifImages;
}
string bitmapToGif (string bitmap) {
//...
return bitmap;
}
void addGif (vector<string> gifImages, string gif) {
sys.append(gifImages, gif);
}
}
</jag>
We’ve used a do-while loop here to synchronize on the completion of all the async calls
issued in the for-loop. The former loop repeatedly puts the main thread into sleep for 10
milliseconds until the gifImages vector is fully populated.
6.3.3 Remote Asynchronous Call
When you deploy an application server, you have the option of configuring it as an async
server. To do so, simply add the following to your server configuration map.
$mode => $async
An async server is capable of handling async remote calls. For each such call, the server
creates a separate thread to handle that call. When a client connects to an async server, it
internally creates a reader thread for handling async calls. The role of this thread is to
invoke the async call’s callback on the client side. An async server handles sync calls like
a sync server – it forces the client to block until the call returns.
To see an example of an async remote call, modify the AsyncTest class as follows:

Add a remote qualifier to the search() method.

Add $mode => $async to your server configuration map.

Deploy a server.

Run a client against it.
You should get the same output as before.
When you use the Flash version of JavaGram runtime, all remote calls must be async,
because Flash is single-threaded and cannot block until a sync call returns.
200
JavaGram Agile Development
6.3.4 Guarded Asynchronous Call
You can also use a guarded version of the asynchronous call syntax to code for both
synchronous and asynchronous clients. For example:
User u = login() ?? sys.async -> setUser(u) -> failed(sys.getAsyncErr());
or
User u = login() ?> sys.async -> setUser(u) -> failed(sys.getAsyncErr());
The condition appearing after ?? or ?> must be a boolean expression. When this condition
evaluates to true, the call is performed as before. When this condition evaluates to false,
login() is called synchronously and no callback is invoked, unless ?> is used, in which
case the callback(s) are still performed but synchronously.
The lib/lang/Common.jag script (described in Chapter 13) provides a Callback class for
use in asynchronous calls. Here is an example of its use in the Quest sample application:
// Snippet from a GUI script that authenticates the user:
User user = User.login(username, password, !sys.async ? null : new Callback() {
public void completed (vague user) {
setCurrUser(user@User);
onLoginOK();
}
public void failed (Exception e) {
onLoginFail(e);
}
});
// Method in User business object that calls a remote method asynchronously.
// Note the use of a guard to call synchronously when there is no callback.
public static User login (string username, string password, Callback cb = null) {
User user @= Object.findOne(User, $username, username)
?> cb != null
-> {
if (!validate(user, password)) {
Exception e = new Exception("invalid username or password");
if (cb == null)
throw e;
else
cb.failed(e);
} else
cb?.completed(user);
} -> cb?.failed();
return user;
}
6.4 Report Generation
JavaGram provides a template-driven report engine that simplifies the task of report
generation. A report template is a document that defines the structure and format of a
Advanced Topics
201
given report type. Within this document, special tags are used to insert JavaGram code
fragments, which perform dynamic computation.
Three types of tags are supported:

Anything that appears within << ... >> is treated as JavaGram code. Such code is
simply evaluated during report generation. Where these tags occur in normal text,
escape them as <\< ... >\>.

Anything that appears within <? ... ?> is also treated as JavaGram code (the content
is escaped if it happens to include markup). Such code is evaluated during report
generation, and the result of evaluation is inserted in the generated report. Where
these tags occur in normal text, escape them as <\? ... ?\>.

The tag <?= ... ?> behaves the same as <? ... ?>, except that the content is not
escaped if it happens to contain markup.
You can create a report template in any of these formats:

RTF (en.wikipedia.org/wiki/Rich_Text_Format). To create an RTF template, use
Microsoft Word, OpenOffice, or any other word processor that supports RTF, and
save the document in Rich Text Format (the report template file must have an rtf
extension). When you generate a report using an RTF template the result will be an
RTF document. You can programmatically convert this document to PDF using
JodConverter (see below).

HTML (en.wikipedia.org/wiki/HTML). This is supported by all web browsers. To
create an HTML template, use an HTML editor or a text editor (the report template
file must have an html extension). When you generate a report using an HTML
template the result will be an HTML document.

XSL-FO (xmlgraphics.apache.org/fop/). To create an XSL-FO template, use an
XML editor or a text editor (the report template file must have a fo extension). When
you generate a report using an XSL-FO template the result will also be a FO
document. You can programmatically convert this document to a variety of printable
formats (including PDF and RTF) using FopConverter (see below).
Of these formats, XSL-FO is the one we recommend most, because it’s very flexible and
there is excellent open source software for it.
It is generally a good idea to use a ‘reports’ directory within your application to hold all
report-related files. For example:

Put your report templates in reports/plate/.

Use reports/compiled/ as the destination directory for compiled report templates.

Use reports/generated/ for storing generated reports.
202
JavaGram Agile Development
The lib/io/Report.jag script provides a number of classes that drive the report engine.
These are described below.
6.4.1 Report Class
This is the base class of a report class generated by the report engine. It has two public
methods:

public static Report factory(string absFilePath, vague data)
This method is also defined in the generated subclass. Call it on the subclass to
instantiate it, passing to it the absolute path for the report file to be generated and the
data variable referred to by the report template.

final void generate ()
Call this method on an instantiated report class in order to generate the report.
6.4.2 ReportEngine Class
This class provides the following static methods for compiling report templates and
manipulating documents.

public static string compileTemplate (string platePath, string scriptPath,
string domain, list loads, string klass)
Before you can generate a report, you must use this method to compile the report
template. This method translates your report template into a JavaGram script that,
when executed, performs report generation. Once a template is compiled, it can be
used as many times as needed to generate reports. It is a good practice to perform
template compilation during application initialization – this will ensure that any
modified report templates are recompiled. However, templates can also be compiled
on the fly to, for example, allow end-user customization.
platePath denotes the report template to be compiled. scriptPath denotes the
JavaGram script to be generated as a result of compilation. domain denotes the
JavaGram domain of the generated script. loads can be used to nominate scripts to be
loaded by the generated script (use it to specify script dependencies). Finally, klass is
the name of the class that subclasses Report in the generated script.
This method returns the absolute path of the generated script (i.e., scriptPath).

public static
dstPlatePath)
map<string,list>
extractMacros
(string
srcPlatePath,
string
Parses the template denoted by srcPlatePath and looks for macros of the form
<#MacroName#MacroDescription#MacroValue#> where:
 MacroName must be a valid identifier, consisting of alphanumeric characters and
underscores, but no blanks.
 MacroDescription can be any string (even empty string). Any literal # characters in
this string must be escaped by a preceding backslash (\).
 MacroValue can be any string. No # should appear in this string.
Returns a map of the form ["MacroName"=>("MacroDescription" "MacroValue"))...].
When dstPlatePath is not null, this file is generated to be a copy of srcPlatePath,
Advanced Topics
203
where each macro has been replaced by its MacroValue. Note: only RTF templates are
supported by this method.

public static string expandMacros (string srcPlatePath, map<string,list>
macros,
string dstPlatePath)
Parses the template denoted by srcPlatePath and looks for RTF fields of the form
{QUOTE MacroName}. In Microsoft Word, you can create such fields using Insert | Field
| Links and References | Quote. In Word 2007, use Insert | Quick Parts | Field... |
Quote. In the displayed dialog, enter the macro name as a single identifier. macros
must be a map that provides a definition of each macro (as returned by
extractMacros()). The file denoted by dstPlatePath is generated to be a copy of
srcPlatePath, where each QUOTE field has been replaced by the value of the
corresponding macro in macros. This method returns dstPlatePath.

public static string mergeRTFs (vague rtfFiles, string mergedRtfPath,
list options = null, list subst = null)
Merges the RTF files denoted by rtfFiles (which should be a list or vector of file
paths) to generate a single RTF file whose path is denoted by mergedRtfPath. options
may contain the following:
 $pageBreak. This causes a page break to be inserted between the merged files.
 $noFileHeader. This causes the merged file to have no file header (this is useful
when your merged file is intended to appear inside another RTF file).
subst may be null or contain two strings, in which case, every occurrence of the first
string is replaced by the second string in the merged file. This method returns the full
path of the merged file (i.e., mergedRtfPath).

public static list getRTFHeaderFooter (string rtfPath)
Returns a list of two strings, the first being the RTF header of the document denoted
by rtfPath, and the second being the RTF footer of the document. Either or both may
be an empty string if the document has no header or footer. Don’t confuse document
header with RTF file header. The former is a paragraph that appears at the top of each
page, whereas the latter is a binary block that appears at the beginning of every RTF
file.

public static string setRTFHeaderFooter (string srcRTFPath, string dstRTFPath,
string rtfHeader, string rtfFooter)
Copies the file denoted by srcRTFPath to the file denoted by dstRTFPath, and in the
process sets the header and footer of the latter to the RTF header/footer denoted by,
respectively, rtfHeader and rtfFooter. If rtfHeader or rtfFooter is null then the
header or footer of the file is left unchanged. If rtfHeader or rtfFooter is "" then the
header or footer of the file is set to an empty header or footer (i.e., a valid RTF header
or footer with no content). This method returns dstRTFPath.
6.4.3 FopConverter Class
This singleton class is a JavaGram wrapping of the open source Apache FOP library
(xmlgraphics.apache.org/fop/) that converts XSL-FO documents into a verity of printable
204
JavaGram Agile Development
formats (GIF, JPEG, MIF, PCL, PDF, PNG, PS, RTF, SVG, TXT, TIFF). Apache FOP is
quite powerful and has the advantage of being open source and free to use.
Please note that the JAR files supplied with FOP must be accessible by a JavaGram
program that uses this class (e.g., via the -jar command line option).
Here is an example to illustrate how FopConverter is used:
FopConverter.singleton.convert("C:/reports/Report1.fo", null,
"C:/reports/Report1.pdf");
The FopConverter class is very simple and has only two methods:

public void convert (string foDocPath, string xsltDocPath, string dstDocPath)
Converts a document from XSL-FO format to another. The destination document
format is determined from the file extension of dstDocPath. The xsltDocPath
parameter should be either null or denote an XSLT stylesheet file.

public void setConfigFile (string configFilePath)
Sets the configuration file for FOP. This is an XML file that specifies how FOP
should be configured if the default configuration is inadequate. For example, you can
use it to specify the fonts that are to be used by FOP. See
xmlgraphics.apache.org/fop/1.0/configuration.html for details. Note that you only
need to call this method once (e.g., from a static initialization block).
Here is a sample FOP XML configuration file, showing how to include fonts:
<?xml version="1.0"?>
<fop version="1.0">
<!-- Strict user configuration -->
<strict-configuration>true</strict-configuration>
<!-- Strict FO validation -->
<strict-validation>false</strict-validation>
<!-- Base URL for resolving relative URLs -->
<base>.</base>
<!-- Font Base URL for resolving relative font URLs -->
<font-base>./</font-base>
<fonts>
<substitutions>
<substitution>
<from font-family="Barcode" font-weight="700..900"/>
<to font-family="Free 3 of 9"/>
</substitution>
</substitutions>
</fonts>
<renderers>
Advanced Topics
205
<renderer mime="application/pdf">
<filterList>
<value>flate</value>
</filterList>
<fonts>
<directory recursive="true">./fonts</directory>
<font kerning="yes"
embed-url="file:///C:/JavaGram/myapp/reports/fonts/msgothic.ttf">
<font-triplet name="MS Gothic" style="normal" weight="normal"/>
</font>
</fonts>
</renderer>
<renderer mime="application/X-fop-print">
<fonts>
<directory recursive="true">./fonts</directory>
</fonts>
</renderer>
</renderers>
</fop>
6.4.4 JodConverter Class
This singleton class is a JavaGram wrapping of the open source JodConverter library
(code.google.com/p/jodconverter) that facilitates document format conversion by running
OpenOffice (openoffice.org) in headless mode. Please note that the JAR files supplied
with JodConverter must be accessible by a JavaGram program that uses this class (e.g.,
via the -jar command line option). Also, OpenOffice 3 or later must be installed on the
machine where conversion is performed.
Here is an example to illustrate how JodConverter is used:
JodConverter.singleton.start();
JodConverter.singleton.convert("C:/reports/Report1.rtf",
"C:/reports/Report1.pdf");
JodConverter.singleton.convert("C:/reports/Report2.rtf",
"C:/reports/Report2.pdf");
//...
JodConverter.singleton.stop();
Because start() is a lengthy method, you should consider calling it only once during
server or application initialization. Once JodConverter is started, convert() calls execute
fairly rapidly. Similarly, stop() should be called when the application or server is about
to exit.
JodConverter supports numerous file formats – see openoffice.org for details. The class
provides the following methods.

public void log (boolean on)
Switches logging on or off. By default, logging is switched off.
206
JavaGram Agile Development

public void start (string officeHomeDir = null, int officePort = 0, int
taskTimeoutMS = 0)
Starts JodConverter which in turn instantiates an OpenOffice process pool for handling
conversions. The parameters can be used to override the default settings for OpenOffice
home directory, the port on which it listens for connections, and conversion task timeout
(expressed in milliseconds).

public void convert (string srcDocPath, string dstDocPath)
Converts a document from one format to another. The source and destination
document formats are determined, respectively, from the file extension of srcDocPath
and dstDocPath.

public void stop ()
Stops JodConverter and clears the OpenOffice process pool.
JodConverter has the advantage of being open source and free to use. There are also
commercial document format conversion tools in the market that you could use instead.
An example is RTF TO XML (www.rtf-to-xml.com) – a Java solution that allows
conversion of RTF to XSL FO, PDF, HTML, and many other printable and viewable
formats. It can be operated from a Graphics User Interface, a command line, or through
Java API. Should you decide to use it, you can use JavaGram’s sys.java() method to
hook into its Java API.
6.4.5 Example
Consider a simple application that generates sales reports for a group of salesmen.
Assume that the following two classes have been defined in a script called Sale.jag:
class Sale {
getable string item;
getable real price;
}
class SaleSummary {
getable string salesman;
getable date endPeriod;
getable vector<Sale> sales;
}
Here is a simple HTML report template for generating a sales report:
<<SaleSummary summary @= data;>>
<html>
<head>
<title>Report Test</title>
</head>
<body>
<h1>Sales Report</h1>
Salesman: <b><?summary.getSalesman()?></b>
Advanced Topics
207
<br>
Sales Period: month ending on <?sys.format(summary.getEndPeriod(), "dd MMM
yyyy")?>
<br>
Sales Summary:
<table border=1 cellspacing=0>
<tr><th width=200>Item</th><th width=100>Price</th></tr>
<<real total = 0;
for (Sale sale in summary.getSales()) {>>
<tr>
<td align=left><?sale.getItem()?></td>
<td align=right><?sys.format(sale.getPrice(), "$0,000.00")?></td>
</tr>
<< total += sale.getPrice();
}>>
<tr>
<td align=right><b>Total:</b></td>
<td align=right><?sys.format(total, "$0,000.00")?></td>
</tr>
</table>
</body>
</html>
The report engine provides two predefined variables:

data, which is of type vague, and represents whatever data the report will need to
access. You can freely refer to this variable in your report template.

file, which is of type stream, and represents the file for the report currently being
generated. You should avoid referring to this variable in your report template.
Here is simple test program that uses some static data to generate a report.
class Test {
static SaleSummary testData = object(SaleSummary
, salesman=>"John Smith"
, endPeriod=>sys.date(2010, 10, 30)
, sales=>[[@Sale item=>"Fridge", price=>1120.5], [@Sale item=>"HiFi",
price=>450.0]]
);
public static void main () {
string platePath = sys.pathConc(sys.root, "reports/plate/Sample.html");
string scriptPath = sys.pathConc(sys.root, "reports/compiled/SampleHTML.jag");
ReportEngine.compileTemplate(platePath, scriptPath, "reports/compiled",
list("reports/Sale"), "SampleHTMLReport");
sys.load(scriptPath, false);
string reportPath = sys.pathConc(sys.root, "reports/generated/SampleGen.html");
Report rep @= sys.call($SampleHTMLReport, "factory", reportPath, testData);
rep.generate();
}
}
208
JavaGram Agile Development
Because the report template refers to classes defined in Sale.jag, we’ve passed this script
to the compileTemplate() method (see the fourth argument) so that it can be loaded
accordingly. The last argument to this method nominates the name of the generated class
that subclasses Report (i.e., SampleReport). Once the report script is generated, we load it
dynamically using sys.load(). The report class is now ready for use and can be
instantiated as many times as needed (once for each report generation). We instantiate
this class dynamically, using sys.call() to invoke its factory() method. Note, how
we’ve passed testData as the last argument in the call. Finally, we can the generate()
method of the class to generate the report. This will produce a HTML file which, when
viewed in a web browser, will look like this:
Reviewing the generated report script will give you a clearer picture of how the report
template is translated into JavaGram code. Here it is:
// Auto-generated by JavaGram -- do not edit
<jag domain="reports/compiled">
<load>
"lib/io/Report"
"reports/Sale"
</load>
class SampleReport extends Report {
public SampleReport (string absFilePath, vague data) {
super(absFilePath, data);
}
public static Report factory (string absFilePath, vague data) {
return new SampleReport(absFilePath, data);
}
protected void _generate () {
SaleSummary summary @= data;
sys.print(file, "<html>\n");
sys.print(file, "<head>\n");
sys.print(file, "\t<title>Report Test</title>\n");
sys.print(file, "</head>\n");
sys.print(file, "<body>\n");
sys.print(file, "<h1>Sales Report</h1>\n");
sys.print(file, "Salesman: <b>");
sys.print(file, summary.getSalesman());
Advanced Topics
209
sys.print(file, "</b>\n");
sys.print(file, "<br>\n");
sys.print(file, "Sales Period: month ending on ");
sys.print(file, sys.format(summary.getEndPeriod(), "dd MMM yyyy"));
sys.print(file, "<br>\n");
sys.print(file, "Sales Summary:\n");
sys.print(file, "<table border=1 cellspacing=0>\n");
sys.print(file, "\t<tr><th width=200>Item</th><th width=100>Price</th></tr>\n");
sys.print(file, "\t\n");
real total = 0;
for (Sale sale in summary.getSales()) {
sys.print(file, "\t\t<tr>\n");
sys.print(file, "\t\t<td align=left>");
sys.print(file, sale.getItem());
sys.print(file, "</td>\n");
sys.print(file, "\t\t<td align=right>");
sys.print(file, sys.format(sale.getPrice(), "$0,000.00"));
sys.print(file, "</td>\n");
sys.print(file, "\t\t</tr>\n");
sys.print(file, "\t\n");
total += sale.getPrice();
}
sys.print(file, "\t<tr>\n");
sys.print(file, "\t<td align=right><b>Total:</b></td>\n");
sys.print(file, "\t<td align=right>");
sys.print(file, sys.format(total, "$0,000.00"));
sys.print(file, "</td>\n");
sys.print(file, "\t</tr>\n");
sys.print(file, "</table>\n");
sys.print(file, "</body>\n");
sys.print(file, "</html>\n");
}
}
</jag>
Here is the same report template defined in RTF format:
210
JavaGram Agile Development
Generating a report using this template and the same data used earlier will produce the
following RTF file.
Finally, the same template can be expressed in XSL-FO as follows.
<<SaleSummary summary @= data;>>
<\?xml version="1.0" encoding="utf-8"\?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<!-- defines the layout master -->
<fo:layout-master-set>
<fo:simple-page-master master-name="first" margin-top="2cm" marginbottom="2cm"
margin-left="2.5cm" margin-right="2.5cm">
<fo:region-body margin-top="3cm"/>
<fo:region-before extent="3cm"/>
<fo:region-after extent="1.5cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<!-- starts actual layout -->
<fo:page-sequence master-reference="first">
<fo:flow flow-name="xsl-region-body" font-family="Helvetica">
<!-- this defines a title level 1-->
<fo:block font-size="18pt" font-family="sans-serif" font-weight="bold"
space-after.optimum="5pt" color="#365F91">
Sales Report
</fo:block>
<fo:block text-align="start">
Salesman: <fo:inline fontweight="bold"><?summary.getSalesman()?></fo:inline>
</fo:block>
<fo:block text-align="start">
Sales Period: month ending on <?sys.format(summary.getEndPeriod(), "dd MMM
yyyy")?>
</fo:block>
<fo:block>
Advanced Topics
211
Sales Summary:
</fo:block>
<!-- table start -->
<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="50mm"/>
<fo:table-column column-width="50mm"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell border-style="solid" border-width="thin">
<fo:block font-weight="bold">Item</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="thin">
<fo:block font-weight="bold" textalign="right">Price</fo:block>
</fo:table-cell>
</fo:table-row>
<<real total = 0;
for (Sale sale in summary.getSales()) {>>
<fo:table-row>
<fo:table-cell border-style="solid" border-width="thin">
<fo:block><?sale.getItem()?></fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="thin">
<fo:block text-align="right">
<?sys.format(sale.getPrice(), "$0,000.00")?>
</fo:block>
</fo:table-cell>
</fo:table-row>
<< total += sale.getPrice();
}>>
<fo:table-row>
<fo:table-cell border-style="solid" border-width="thin">
<fo:block font-weight="bold" textalign="right">Total:</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="thin">
<fo:block text-align="right"><?sys.format(total,
"$0,000.00")?>
</fo:block></fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
Generating a report using this template with the data used earlier and converting the
result to PDF will produce the following output.
212
JavaGram Agile Development
6.5 Web Services
Once you’ve developed a JavaGram application server, you can expose the functionality
provided by the server to other systems via web services. The primary advantage of using
a web service as an interface is technology independency – the service can be accessed
by any other system, regardless of its underlying technology, so long as it supports web
services. The inherent loose-coupling of web services makes them the preferred
integration method for web applications. For example, you can use web services to
integrate a web site developed in Java, .NET, PHP, or any other similar technology, with
a JavaGram server. To do this, you don’t need to write any additional JavaGram code, but
simply configure your application server appropriately.
6.5.1 Architecture
The JavaGram web service is packaged as a servlet (JAGWS.war) that must be deployed
into an Apache Tomcat/AXIS2 environment. The JAG runtime is packaged into this
WAR file and is responsible for loading/compiling/executing JavaGram server scripts in
response to a client requests. This process may also involve accessing external databases
or systems in the same manner as a normal JavaGram application server.
6.5.2 JavaGramService
JavaGram provides a single web service (called JavaGramService) that manages all ‘web
services’ communication. A client first calls the authenticate() method of this service to
obtain a valid ‘client reference’ for use in subsequent calls. The generic method
methodCall() can be used to access any exposed server-side functionality. Additionally,
downloadFile() and uploadFile() allow you to download/upload files from/to the server.
Advanced Topics
213
6.5.2.1 JavaGramService Methods
For ease of description, the service methods are described here using Java syntax. Please
refer to the WSDL file for their platform-independent syntax.
public String authenticate (String username, String password) throws Exception
SUMMARY:
Before using the JavaGramService, a client must be authenticated. The
required username and password can be defined in the server config file.
If not defined, any username and password combination will be accepted.
If successful, authenticate will return a ‘client reference’ as a string,
which you will use in subsequent calls. Otherwise, it will throw an
exception. You only need to call this method once.
EXAMPLE:
String client = authenticate("soap", "secret");
public String methodCall (String client, String call)
SUMMARY:
Requests that the JavaGramService performs the method call denoted by
call, and returns the result of the call. For client, you must pass a client
reference returned by an earlier call to authenticate(). Both call and the
returned result are expressed in XML (see the next sub-section for syntax
definition).
EXAMPLE:
String xml = methodCall(client,
"<request id='1' class='crm.bom.User' method='findById'>" +
"<arg type='int' value='1'/>" +
"</request>"
);
public byte[] downloadFile (String client, String relPath) throws Exception
SUMMARY:
Attempts to download the file whose server-side relative path is denoted
by relPath. For client, you must pass a client reference returned by an
earlier call to authenticate(). If successful, it returns the file content as a
binary array, which is encoded as a Base64 string. Otherwise, it throws an
exception.
EXAMPLE:
byte[] data = downloadFile(client, "crm/rep/Sales.pdf");
public String uploadFile (String client, String relPath, byte data[]) throws Exception
SUMMARY:
Attempts to upload the file whose content is denoted by data to the serverside location denoted by relPath. For client, you must pass a client
reference returned by an earlier call to authenticate(). Encode the data as
a Base64 string before passing it to this method. If unsuccessful, it throws
an exception. This method always returns null.
EXAMPLE:
214
uploadFile(client, "crm/data/Orders.xml", data);
JavaGram Agile Development
6.5.2.2 XML Syntax for methodCall()
The second argument of methodCall() must be expressed in XML, and define a serverside method invocation request. For example, suppose that we have a server-side
JavaGram script (crm/bom/Client.jag) that defines a Client class:
class Client {
//...
public static find (string name, string postcode) {
//...
}
public void update (int by) {
//...
}
}
Assuming that the server has been configured to expose these methods via web services,
the following represents valid XML to be passed to methodCall().
<request id="12" class="crm.bom.Client" method="find">
<arg type="string" value="Smith"/>
<arg type="string" value="3104"/>
</request>
The XML call must always be a <request> element and the result will always be a
<response> element, having the same id as its corresponding request. For example:
<response id="12">
<return type="crm.bom.Client">
<joined type="date" value="2010-09-01 16:05:40"/>
<id type="int" value="1012301"/>
<name type="string" value="Smith"/>
<postcode type="string" value="3104"/>
<status type="string" value="Active"/>
</return>
</response>
For a non-static method, the request must also include the object on which the method is
invoked (typically returned by an earlier call).
<request id="13" class="crm.bom.Client" method="update">
<this type="crm.bom.Client">
<joined type="date" value="2010-09-01 16:05:40"/>
<id type="int" value="1012301"/>
<name type="string" value="Smith"/>
<postcode type="string" value="3104"/>
<status type="string" value="Active"/>
</this>
<arg type="int" value="10221"/>
</request>
Advanced Topics
215
In this case, the response will also include the modified <this> returned by the server.
The following two tables specify the permissible composition of request and response
elements.
<request> Parts
Description
id="..."
A client-supplied identifier, used for the purpose of matching a
request/response pair. Example: id="10"
script="..."
The server-side script containing the referenced class. You can omit
this property if the script path matches the class path. Example:
script="crm/bom/Client"
class="..."
The name of the referenced class. For a qualified name, you can use
backslash or dot as the separator character. So, for example,
class="crm\\bom\\Client" and class="crm.bom.Client" are
equivalent (the latter is internally replaced by the former). Also, if a
script is specified, you can use the unqualified class name instead
(e.g., class="Client").
method="..."
The class method to be called. Example: method="find"
minimal="..."
When set to true, it causes a non-static call not to return the <this>
element (defaults to false). Example: minimal="true"
indent="..."
When set to true, it causes the response XML to be indented for
readability (defaults to false). Example: indent="true"
<this>
A non-static call must include a <this> element to represent the
object on which the method is to be called.
<arg>
Specifies an argument to be passed to the method. Arguments must
appear in the order in which they are to be passed to the method.
Example: <arg type="int" value="10221"/>
<response> Parts
Description
id="..."
Response identifier, matching the corresponding request. Example:
id="10"
error="..."
When the request fails, this property is present and denotes the error
message. In this case, the body of the response will be empty.
<this>
Present only in response to a non-static call, and represents the
modified object.
<return>
Present only when the method has a non-void return type, and
represents the value returned by the method.
216
JavaGram Agile Development
6.5.2.3 XML Representation of JavaGram Data
Representation of JavaGram data in XML is governed by the following rules.
A data item is represented by an element having these properties:

type, which represents the data type (e.g., "int", "map"). For parameterized
containers, parentheses are used instead of angled brackets to avoid clashing with
XML syntax (e.g., "map(symbol,real)"). For qualified class names, backslash as well
as dot can be used as separator (e.g., "crm\\bom\\Client" or "crm.bom.Client").

value, which represents the value of an atomic data item (i.e., int, real, char, boolean,
string, symbol, date). This also includes vague, provided it refers to atomic data. This
property is not present for non-atomic data. Instead, the data appears as children of
the element.

keytype, is present for a map element and represents the type of the map element key.
This is required to be an atomic type.

key, is present for a map element and represents the value of the map element key.
Non-atomic data is represented by nested elements. However, a null non-atomic data item
is represented by an element whose value="null", having no child elements. In the
following descriptions, where an element’s name appears as <name ...>, it is determined
by the context. For example, for a return value the element will be <return ...>, for an
argument value, it will be <arg ...>, and for an object field it will be the field name.
A vector is represented like this:
<name type="vector">
<elem type="..." value="..."/>
...
</name>
A list is represented like this:
<name type="list">
<elem type="..." value="..."/>
...
</name>
A map is represented like this:
<name type="map">
<elem keytype="..." key="..." type="..." value="..."/>
...
</name>
An object is represented like this:
Advanced Topics
217
<name type="...">
<fieldName type="..." value="..."/>
...
</name>
6.5.3 Software Installation
To deploy a web service, you need to have Apache Tomcat and AXIS2 installed on the
server host. For simplicity, the following instructions assume that the server name is
localhost.
Download the Apache Tomcat server from http://tomcat.apache.org and extract it to a
directory (e.g., C:\Program Files\tomcat).
To run Tomcat, make sure that you have the following environment variables correctly
defined. On Windows, you can set these via “System Properties | Advanced |
Environment Variables”. Define them as system variable.

JAVA_HOME (e.g., set to C:\Program Files\Java\jdk1.6.0_16), or

JRE_HOME (e.g., set to C:\Program Files\Java\jre6), and

CATALINA_HOME (e.g., set to C:\Program Files\tomcat).

CATALINA_OPTS (e.g., set to -Xms256m -Xmx512m). On a 64-bit server, also consider
using the incremental garbage collection option (-Xincgc)
To install Tomcat as a service, start a DOS session and run the command
‘tomcat\bin\service install’. The service is installed but not started (needs manual
startup). Go to “Control Panels | Admin Tools | Services” and start the Apache Tomcat
service.
Verify that Tomcat is running by entering the URL http://localhost:8080/ in a browser.
You should see a page like the following.
218
JavaGram Agile Development
To install AXIS2, download the complete binary distribution from
http://ws.apache.org/axis2 and extract it to a directory (e.g., C:\Program Files\axis2).
Also download the Axis2 WAR distribution from the same site (this is a zip file from
which you need to extract axis2.war). Alternatively, you can build the WAR file from the
binary distribution, as per the instructions on the web site.
Install the AXIS servlet by copying axis2.war to Tomcat’s webapps directory. If Tomcat
is running, it will automatically install the WAR file. Start a web browser and go to the
AXIS URL (e.g., http://localhost:8080/axis2/). The following page should be displayed.
Edit Tomcat’s webapps\axis2\WEB-INF\conf\axis2.xml file and make sure the
hotupdate
parameter
is
set
to
true:
<parameter
name="hotupdate">true</parameter>
Advanced Topics
219
Similarly, install the JAGWS servlet by copying JAGWS.war from the JavaGram
installation directory to Tomcat’s webapps directory. Start a web browser and go to the
JAGWS URL (e.g., http://localhost:8080/JAGWS/). A page similar to the above should
be displayed. Click on ‘Services’ – you should be able to view the JavaGramService
methods.
After defining the JAGWS_OPTIONS environment variable (see below), in the same browser
window, enter the following URL to view the service WSDL file:
http://localhost:8080/JAGWS/services/JavaGramService?wsdl
6.5.4 Configuration
A number of configuration steps are required in order to deploy a functioning web
service.
6.5.4.1 Server Configuration
The JavaGram server configuration file must have a $service entry that defines the
security requirements of the JavaGramService as well as what is to be exposed.
Here is an example:
$service=>[
$security=>[
$username=>"soap", $password=>"secret"
]
, $classes=>[
"crm\\bom\\User" => ["findById", "getPermissions"]
, "crm\\bom\\Client" => "*"
220
JavaGram Agile Development
]
]
The $security entry is optional and, if not present, will allow any username/password
combination to succeed. When present, the username and password provided by the web
service client in the authenticate() call must match in order for client access to be
granted. This password denoted by $password may optionally be enclosed in parentheses
to signify that it is encrypted. Where using encrypted passwords, the command line
parameter, -passKey, must be used (in the JAGWS_OPTIONS environment variable) to specify
the key to be used to decipher.
The $classes entry nominates the classes that are to be exposed via the web service. Each
entry in this map, maps a qualified class name to a vector of methods that are to be
exposed. If you want to expose all the exposable methods of a class, use the wildcard "*"
instead of a vector.
The following rules apply to method exposure:

A non-public method is never exposed.

An slocal method is never exposed.

If an exposed method has multiple overloaded public versions, all are exposed.
It is generally a good practice to only expose methods of business objects.
The downloadFile() and uploadFile() web service calls are governed by the $download
and $upload entries in the server config file (as explained in Chapter 7) much in the same
way as sys.download() and sys.upload().
6.5.4.2 JAGWS Configuration
When deploying your web service, you must make sure that the JAG runtime options are
specified. You can specify these by defining an environment variable called
JAGWS_OPTIONS. The relevant runtime options are (see Chapter 7):
-verbose, -noWarn, -keepStdOut, -clearCache, -root,
-stage, -config, -passKey, -logfile, -tz
For example:
JAGWS_OPTIONS=-noWarn -root "C:/CRM/" –stage test -config crm/Config.jag
6.5.4.3 Additional JAR files
If you have additional JAR files that need to be accessed by your server (e.g., JDBC
drivers, FOP library), you can deploy these in one of two ways:

Add the files to JAGWS.war (at the same level as JAG.jar) before you deploy JAGWS.war.
Advanced Topics
221

Manually add the files to Tomcat’s webapps\JAGWS\WEB-INF\lib\ directory after
deploying JAGWS.war. (Note: the Eclipse build process for JAGWS.jar doesn’t update
the JAR files in it correctly, unless they’re manually inserted into the project’s WEBINF/lib folder manually.) It’s important to keep webapps\JAGWS\WEB-INF\lib\ up-todate for future builds (e.g., with new JAG.jar or new 3rd party JAR files). Also, make
suer that you don’t place JADE.jar in this folder as it will take precedence over
JAG.jar!
6.5.4.4 Virtual Directory
If you intend to generate reports/images on the server side and view them on the client
side, the most efficient method is to create a virtual directory in your web server.
6.5.5 Virtual Directory
You need to setup a virtual directory within the web server of the Rux Server host. When
Rux generates a report, it publishes it in this virtual directory so that it can be accessed in
a web browser. You can either do this using IIS or Tomcat (IIS is recommended for a
production/test server; Tomcat is recommended for a development server).
To setup a virtual directory using IIS:
 Login to the server host and run the Server Manager.

Select Roles / Web Server (IID) / Internet Information Services (IIS) Manager

In the right-hand pane, right click on Sites / Default Web Site and select Add Virtual
Directory from the popup menu.

Enter the virtual directory details as illustrated below and press OK.
To setup a virtual directory using Tomcat, open Tomcat’s conf/server.xml file in a text
editor and add the following XML element as a child of the <Host> element. Then, restart
Tomcat.
<!-- Add a virtual directory called /data -->
<Context path="/data" docBase="C:/JavaGram/app/data" reloadable="true">
222
JavaGram Agile Development
</Context>
Where, for example, C:/JavaGram/app/data is the folder you’re publishing as /data. Now
if you, for example, generate the report C:/JavaGram/app/data/report/Sample.html, you’ll
be able to access it using the URL http://hostname:8080/data/report/Sample.xml. The
Desktop class in lib/lang/Common makes it easy to view the report. The following call
opens the file in a new browser window.
Desktop.browse(url, "_blank")
6.5.6 Client Example
The instructions in this section assume a Java client developed using Eclipse enterprise
edition. We also assume that you have a JavaGram server-side script called
crm/bom/Client similar to what was outlined earlier in this section.
The starting point for the web service client is the WSDL file. In Eclipse:

Go to ‘File | New | Other | Web Services | Web Service Client’.

Enter
the
WSDL
file
into
the
Service
definition
field.
For
example:
http://localhost:8080/JAGWS/services/JavaGramService?wsdl

Make sure that the Server link is set correctly to Tomcat.

Click on the Web service runtime link and make sure it’s set to ‘Apache Axis2’.

Click on the Client project link and enter ‘JAGWS_client’. Set the project type as
‘Dynamic Web project’.
Press Finish. The client stub files will be generated into your JAGWS_client project. Now
we can write a client class that uses these stubs to communicate with the deployed
service.
public class JavaGramTestClient {
static public void main (String args[]) {
try {
JavaGramServiceStub stub = new JavaGramServiceStub();
Authenticate auth = new Authenticate();
auth.setUsername("soap");
auth.setPassword("secret");
AuthenticateResponse ar = stub.authenticate(auth);
String client = ar.get_return();
String xml =
"<request id='1' class='crm.bom.Client' method='find' indent='true'>" +
"<arg type='string' value='Smith'/>" +
"<arg type='string' value='3104'/>" +
"</request>";
Advanced Topics
223
// ---- Synchronous call example ---MethodCall call = new MethodCall();
call.setClient(client);
call.setCall(xml );
MethodCallResponse cr = stub.methodCall(call);
String str = cr.get_return();
System.out.println(">> Synchronous call returned: ", str);
// ---- Asynchronous call example ---MethodCall asyncCmd = new MethodCall();
asyncCmd.setClient(client);
asyncCmd.setCall(xml);
JavaGramServiceCallbackHandler cb = new JavaGramServiceCallbackHandler() {
public void receiveResultmethodCall(MethodCallResponse result) {
String str = result.get_return();
System.out.println(">> Asynchronous call returned: " + str);
}
public void receiveErrormethodCall(Exception e) {
System.out.println(">> Asynchronous call failed: " + e.getMessage());
}
};
stub.startmethodCall(asyncCmd, cb);
// Avoid exiting program before callback returns:
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
This will produce an output, such as:
>> Synchronous call returned: <?xml version="1.0" encoding="UTF-8"?>
<response id="1">
<return type="crm.bom.Client">
<joined type="date" value="2010-09-01 16:05:40"/>
<id type="int" value="1012301"/>
<name type="string" value="Smith"/>
<postcode type="string" value="3104"/>
<status type="string" value="Active"/>
</return>
</response>
>> Asynchronous call returned: <?xml version="1.0" encoding="UTF-8"?>
<response id="1">
<return type="crm.bom.Client">
<joined type="date" value="2010-09-01 16:05:40"/>
<id type="int" value="1012301"/>
<name type="string" value="Smith"/>
<postcode type="string" value="3104"/>
<status type="string" value="Active"/>
224
JavaGram Agile Development
</return>
</response>
6.6 Efficiency Considerations
JavaGram has a number of built-in features to improve the runtime efficiency of your
applications. Some of these apply at all times and are outside the programmer’s domain
of control, while others can be configured via runtime options or server configuration.
This section describes these features, as well as design and coding guidelines that will
assist you in developing better performing applications.
6.6.1 System-level Caching
When you deploy an application in client-server mode, JavaGram maintains a file cache
on both ends, the basic purpose of which is to avoid unnecessary reprocessing and/or
transmission of files that have already been processed. The server-side cache manages
compiled scripts generated for consumption by clients or servers. The client-side cache
(not applicable to the Flash version of JavaGram runtime) manages compiled scripts
downloaded from the server. All scripts (in source or compiled format) are time-stamped
to keep track of their currency. JTP uses this information to ensure that a script is
transmitted only when it’s out of date with respect to what the target cache holds.
This caching mechanism is part of JavaGram’s design and outside programmer’s control.
For further details, please refer to Section 7.5.
6.6.2 System-level Compression
When deploying a server, you can configure it to compress transmittable data that
exceeds a certain size. This is controlled by a $zip entry in the server configuration map.
For example, adding
$zip => [$min => 1024]
Advanced Topics
225
to your server configuration map will ensure that all files and messages exchanged
between clients and server that exceed 1024 bytes will be compressed. Compression is
applied in both directions and is totally transparent. For example, if a client requests a file
from the server that is larger than this size, the server will compress the file, send it
across to the client, where it will be automatically decompressed and then handed over to
the caller. The same applies to remote calls. For example, if a client makes a remote call
where the overall size of the call packet exceeds 1024 bytes (e.g., because one of the
arguments is a large vector) then the call packet will be compressed before being sent to
the server, and decompressed on the server side before it’s dispatched to its target.
Because a large part of a client-server application’s latency is due to data transmission
over networks, compression can pay handsome dividends. So its use is highly
recommended.
6.6.3 Application-level Caching
In most applications there are application-specific opportunities for further caching to
improve performance, be it at the client end, the server end, or both. The most common
example of this is the management of low-volatility reference data. For example, an
application that uses suburb and postcode data often stores this information in a database
table. Given the low volatility nature of this data, it would be wasteful to query this table
every time the application needs to look up a suburb. This information can be retrieved
once and held in a cache for fast access.
For most cached information, you also need some way of refreshing the cache such that it
can be activated by an automatic or manual trigger. Unless your cached data has zero
volatility, there always comes a time when updates need to be reflected. The fallback
position of restarting the application or the server is not an attractive solution.
You should always treat the use of an application-level cache as a trade off between
performance and memory consumption. Therefore, you should be selective and opt for
choices that are frequently used but not too memory hungry.
6.6.4 Remoting Performance Factors
When designing remote methods, you should think carefully about the implications of
making a remote method static or non-static. Generally, a static remote method executes
more efficiently because there is no implicit object to be transmitted back and forth.
However, if the object is likely to be modified on the server side, then a non-static
method is the right approach, as it will ensure that the client-side object gets updated
correctly.
Here are some guidelines to assist you in deciding between the two options:

Search methods are generally best defined as static because of their read-only nature.

If an object is very large then you would really want to avoid invoking a non-static
remote method on it – the transmission overheads will be substantial. Consider other
226
JavaGram Agile Development
options, such as defining it as a remote class, or using a static remote method, or
redesigning it as a collection of smaller classes.

Be careful with passing large objects as arguments to remote methods, or returning a
large object as result. Sometimes this is unavoidable but often, with a little bit of
thinking, a better approach can be found.

When coding enable, visible, and event handlers for GUI elements, as far as
possible, avoid calling remote methods. These handlers may get called very
frequently, leading to a lot of traffic.

Wherever possible, avoid looping around remote calls. For example, if a remote call
is going to return the same value every time round the loop and causes no server-side
update, you can safely move the call outside the loop.
Advanced Topics
227
7 Deployment
This chapter describes the process of deploying JavaGram applications, and what you
need to consider during development in order to maximize deployment flexibility. One of
the major benefits of JavaGram is deployment simplicity. Because JAG incorporates an
applications server as well as a proxy server, all deployments use the same runtime
environment, regardless of the model.
The recommended development approach is to initially treat the application as standalone
so that you can focus on realizing business functionality. You should also consider what
methods/classes should be defined as remote but not labor too much on this issue. Once
the application has been sufficiently developed, you should revisit the ‘remote’ design
decisions and test the application as client-server so that any design oversights regarding
distribution can be identified and resolved.
7.1 Running a Program
There are three distinct modes for running a JavaGram program:

To run a (standalone or client) program that has no GUI, or the JavaGram compiler,
use the jag.run.Env Java class.

To run a (standalone or client) GUI program, use the jag.gui.Gui Java class.

To run a server program use the jag.run.svr.Server Java class.
Examples:
java -cp JAG.jar jag.run.Env InitialScript
javaw -cp JAG.jar jag.gui.Gui InitialScript
java -cp JAG.jar jag.run.svr.Server InitialScript
where InitialScript is the relative path of the script that boots the application or the
server (e.g., quest/Quest.jag). It’s important to note that a GUI program must be run
using javaw (not java), otherwise it will abort.
7.1.1 JavaGram Options
You can use a range of command line options when running a JavaGram program. For
example, in
java -cp JAG.jar jag.run.Env –root C:/JavaGram quest/Quest.jag
we’ve used the –root option to specify the root path C:/JavaGram.
228
JavaGram Agile Development
The following options are available.

The –root option sets the sys.root attribute to a given path. It must be followed by a
valid directory path. If the path contains spaces, enclose it in double-quotes. If not
specified, sys.root defaults to the directory in which you run the program. When
JavaGram processes a relative path, it assumes that it’s relative to the root directory.
The root path doesn’t need to end in / (a slash is added automatically where needed).

The –host option is used by clients and servers, and sets the sys.host attribute to a
given host. It must be followed by a host specification in the format host:port (where
host is the name or IP address of a computer, and port is a port number). If host:port
contains spaces, enclose it in double-quotes. If not specified, sys.host defaults to
localhost:443. You can also specify multiple alternate hosts (in which case, you
should enclose the whole thing in double-quotes). If you separate the hosts with
spaces, commas, semicolons, or | then each host is tried in turn until one succeeds
(i.e., failover treatment). If you separate the hosts using a plus character (+) then all
hosts are tried and the one with the lowest load is selected (i.e., servers are load
balanced from the client side). The recommended approach for load balancing,
however, is to use a proxy server (see Section 7.2.2).

The –ssl option is used by client applications, and sets the sys.ssl attribute to a given
keystore. It must be followed by a keystore path (absolute or relative to sys.root, for
the test certificate which you have self-signed), or just "" (if you have a digitallysigned certificate). If not specified, sys.host defaults to null, implying that the client
should use a normal (i.e., non-SSL) channel for connecting to the server.

The –boot option is used by client applications, and sets the sys.boot attribute to a
script. It must be followed by a file path relative to sys.root, which represents the
application’s main script. The file path may be optionally preceded by a partition
name, in which case the two must be separated by a colon. If the script path contains
spaces, enclose it in double-quotes. If the script path has no file extension, it will be
resolved automatically to a .jag or .jax file. This option also requires the –host
option to be present. It causes the client to negotiate with the server to boot the
application, without initially having any of the application code on the client-side. If a
partition is specified, that partition is downloaded first and processed. The script is
then either sourced from the partition or downloaded, and then run.

The –sboot option (strict boot) is used by client applications, and has the same effect
as the –boot option, except that if the server to which the client points is a proxy
server then the boot is strictly performed against the proxy server itself (and not one
of the end-servers). This option is useful for running the JavaGram Server Monitor
such that it monitors a proxy server. Any subsequent calls to the sys.client() method
from such a client environment assume a strict boot. Using -sboot against an
application server is harmless.

The –config option is used by a non-client (servers and standalone applications) for
specifying the application runtime configuration. It must be followed by a file path
Deployment
229
(absolute or relative to sys.root) which contains the configuration map. If the file
path contains spaces, enclose it in double-quotes. See Section 7.2.1 for details.

The –passKey option can be used by non-client (servers and standalone) applications.
It must be followed by a key file path (absolute or relative to sys.root. This key is
later used for deciphering encrypted passwords for database connections and web
services specified in configuration files.

The –debug option is for the internal use of JADE when it runs an application in
debug mode.

The –verbose option sets the sys.verbose attribute to true. It tells JavaGram to output
additional information for certain activities. For example, it causes the sys.load()
method to print the name of the file it loads, and the sys.appServer() method to print
the client-server communication.

The –noWarn option sets the sys.warning attribute to false. It tells JavaGram to
suppress warning messages.

The –noPart option is applicable to clients. It’s mainly used in application testing
situations and tells JavaGram to ignore partitions.

The –stage option must be followed by one of dev, test, or prod and sets the
sys.stage attribute to one of: $dev, $test, or $prod. By using a stage that reflects the
current state of your software, you can avoid inadvertent mistakes such as releasing a
script intended for system test into production.

The –logfile option is used for redirecting the standard output and standard error to a
log file. It must be followed by an absolute log file path. If the file path contains
spaces, enclose it in double-quotes. If the file already exists, the existing file is
renamed to <file>.old. If the latter already exists, it is first removed. Alternatively,
you can use a date pattern in the log file name (e.g., C:/app/Log<yyyy-MM-dd>.txt).
This causes old log files not to be renamed or deleted. Instead, the new log file is
named using the current date and the specified date pattern. You can also specify a
log file age limit in the server configuration, so that new log files are created
automatically (e.g., one a day).

The –keepStdOut option can be used in conjunction with the –logfile option. It causes
standard output to be sent to the log file as well as standard output.

The –tz option is used for specifying timezone. It must be followed by a timezone
region (e.g., "America/Los_Angeles"). All times calculated within the application will
conform to this timezone.

The –minZip option is used for specifying a minimum limit for compressing clientserver data exchanges – any data (message content or file) below this limit is not
compressed. It must be followed by an integer value (defaults to 1024 bytes). This
option applies to servers (you can also override it in the server configuration). Clients
obtain the same value from the server during initial handshake and use it
subsequently.
230
JavaGram Agile Development

The –clearCache option can be used for clearing the JavaGram file cache on client or
server side. It takes no argument.

The –c option takes no argument and causes nominated JavaGram source files (see
Section 7.1.3) to be compiled rather than loaded.

The –r option takes no argument and applies to loading or compiling files. If the
source is a directory then it’s processed recursively (i.e., the files in the directory and
the files in all its sub-directories, and so on).

The –cScope option takes a file path as argument and assumes that it contains a list of
files to be compiled, each specified on a separate line using either its absolute or
relative (to sys.root) path.

The –cToDir option takes a directory path as argument and treats that directory as the
target for compiled files. If the directory path contains spaces, enclose it double
quotes. If no compilation target directory is specified then compiled files will be
written to the same directory as the source files.

A file path on its own (either absolute or relative to sys.root) is assumed to be a
JavaGram script and is loaded (or compiled if the –c option is also present).
7.1.2 Java Options
In the command line that you use to run a JavaGram program, you can also specify Java
runtime options. You’ve already seen the use of the –cp option to specify the class path
for JAG.jar. Consult the Java documentation for other available options. The following
Java options are commonly used for running JavaGram programs:

The –client option selects the ‘client’ Java VM. This is the default VM.

The –server option selects the ‘server’ Java VM.

The –Xms option is used for specifying the initial Java heap size (e.g., -Xms256m).

The –Xmx option is used for specifying the maximum Java heap size (e.g., -Xmx1024m).

The –Xrs option reduces the use of OS signals by Java VM.
7.1.3 Compilation
To compile a JavaGram program, use the –c option. For example,
java –cp JAG.jar jag.run.Env -c C:/JavaGram/quest/Quest.jag
compiles Quest.jag to produce Quest.jax in the same directory. Alternatively,
java –cp JAG.jar jag.run.Env -c –r C:/JavaGram/quest/
recursively compiled all files in the specified directory. To send the result of compilation
to a different directory, use the –cToDir option.
Deployment
231
Another method of specifying the files to be compiled is to list them in a scope file. For
example, in
java –cp JAG.jar jag.run.Env -c –cScope C:/JavaGram/BuildScope.txt
the file appearing after –cScope contains the list of files to be compiled. In this scope file,
each file must be specified on a separate line, using either its absolute or relative (to
sys.root) path. This method can also be combined with the above methods.
Using compiled code has two advantages:

Compiled code is more secure, especially when you intend to package and distribute
it to other parties.

Compiled code runs faster because the JAG runtime doesn’t have to do as much
processing and analysis.
Within JAG, compilation is managed by a module called codec (coder/decoder), which
handles both the encoding of clear text scripts into binary format, and the decoding of
binary scripts such that they can be loaded. The codec is version controlled. If, as a part
of the JAG maintenance cycle, a codec rule is changed, the codec version number is
incremented. Each compiled script records the codec version with which it has been
compiled. This is internally used for consistency checking.
7.2 Server Deployment
Before deploying a server, make sure that you have JRE and JAG installed on your
machine.
7.2.1 Application Server
A JavaGram application server is totally generic – it makes no assumptions about what
particular application it’s going to support. As far as the application server is concerned,
it hosts a collection of JavaGram scripts (similar to the way a web server hosts a
collection of web pages) and responds to client requests to perform operations on these
scripts (respond to download requests, perform remote calls, etc.) Consequently, a single
application server can host many different applications with potentially overlapping
functionality.
232
JavaGram Agile Development
Application server deployment involves two scripts:

The library script lib/svr/AppServer.jag creates a server by calling the
sys.appServer() method. The server awaits client connection requests on a nominated
port, and responds to a successful connection request by creating a session thread and
handing over the client to that thread.

A server configuration script (e.g., quest/Config.jag) which defines important
runtime parameters. The parameters are organized as a map, whose keys denote the
parameter names.
To illustrate the simplicity of lib/svr/AppServer.jag, we’ve included its code below.
<jag domain="lib/svr">
// Use this class to deploy an application server.
class AppServer {
public static void main () {
vector<string> host = sys.strSplit(sys.host, ":");
if (sys.length(host) < 2) {
sys.println(sys.err, "invalid -host option: ", sys.host);
sys.exit(1);
}
string hostName = host[0];
int port = sys.toNum(host[1]) @int;
symbol mode = sys.config[$mode] == $async ? $async : $sync;
// Infinite loop will handle server resets:
for (;;) {
try {
sys.println("=============== APP SERVER STARTING ===============");
sys.appServer(hostName, port, mode);
}
catch (Exception e) {
sys.println(sys.err, "APP SERVER ABORTED; reason: ", e.getMessage());
sys.exit(2);
}
}
}
}
</jag>
Note how an infinite for-loop is used around the call to sys.appServer(). This is because
a server can be reset via the JavaGram Server Monitor, causing this method to return, in
which case the method is invoked again.
To run an application server, use lib/svr/AppServer.jag as your main script. Specify the
server host, server configuration (see below), and any other required options in your
command line. For example:
Deployment
233
java -cp JAG.jar jag.run.svr.Server -root "C:/JavaGram" -config Config.jag -host
"localhost:443" "C:/JavaGram/lib/svr/AppServer.jag"
Please note that for servers, the security settings are not specified via the –ssl command
line option, but in the server configuration file.
The configuration file parameters can be a combination of standard and applicationspecific parameters. If you want to build your configuration map dynamically, create the
config file as a normal script and define a class within that script, having a static main
method that returns the config map.
Here is an example of a configuration file that simply contains a map:
[
$download =>
[$default => ["jag" => $private, "*" => $public]
,"quest/gui/" => $public
,"quest/gui/Rights.jag" => $private
,$cache => "mycache/"
]
,$upload =>
[$default => ["*" => $public]]
,$policy => "flash/SocketPolicy.xml"
,$security =>
[$keystore => "ssl/JAG.jks"
,$password => "changeit"
,$trusted => ["quest/Quest.jag", "monitor/Monitor.jag"]
,$exclude => ["testhost:443", "testhost:444"]
]
,$mode => $sync
,$variant=>[$portPlus=>100, $secure=>false, $async=>true]
,$partition =>
[$cache => "parts"
,$exclude => [".svn/*", "*.jagzip"]
,"QuestPart" =>
[("quest/", $recursive, "*.jag", "*.html", "*.gif")
,("lib/", $recursive, "*.jag", "*.gif")
]
]
,$session => [$timeout => 300, $error => 10] // 5 minutes timeout
,$log => [$timeout => 86400]
// 1 day per log file
,$zip => [$min => 1024]
,$load => "ServerInit.jag"
,$database =>
[$mysqlDb =>
[$name => "MySql", $url => " jdbc:mysql://localhost/quest "
,$user => "root", $password => "password"
,$timeout => [$login => 30, $query => 20]
,$driver => "com.mysql.jdbc.Driver"
]
234
JavaGram Agile Development
,$oracleDb =>
[$name => "Oracle", $url => "jdbc:oracle:thin:@localhost:1213:quest"
,$user => "test", $password => "secret"
,$timeout => [$login => 30, $query => 20]
,$driver => "oracle.jdbc.OracleDriver"
,$schema => "Quest"
]
]
]
You need to ensure that:

The $download entry reflects the desired access rights to server-side files, which may
be private (inaccessible) or public (accessible). The above map, for example, specifies
that by default ($default) all .jag files are private and all other file types (*) are
public. This default behavior is overridden by the next two entries: all files in the
quest/gui/ directory are public, except for quest/gui/Rights.jag which is private. As
a general rule any files types not covered by the rules default to private. Note that, to
execute, a client doesn’t need source (.jag) files; it always uses compiled (.jax) files
instead. Making .jag files private protects you against a client attempting to access
your source code. The $cache entry (within $download) specifies a directory for serverside file caching. If not specified, the directory <root>/cache/ is used instead.

The $upload entry is similar to the $download entry in its syntax, and optionally
specifies what a client application can upload to the server. Files specified as $public
can be uploaded, whereas files specified as $private are blocked.

The $policy entry optionally specifies a socket policy XML file for access by Flash
clients. The path to the policy file may be absolute or relative to sys.root. See
https://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html for more
details on socket policy files. If no policy file is specified, the following inbuilt policy
is used instead:
<cross-domain-policy>
<allow-access-from domain="*" to-ports="*"/>
</cross-domain-policy>

The $security entry optionally specifies the security configuration of the server. The
above example provides SSL security using a certificate stored in the nominated
keystore. The path to the keystore may be absolute or relative to sys.root. The
$password is needed but of no consequence. The $trusted vector is optional, and can
be used to enforce additional security. When specified, a client application must use
only one of the specified relative scripts as its main script. Otherwise, a client
application can start with any script. The $exclude vector is optional, and can be used
to specify hosts that don’t use SSL security.

The $mode entry specifies the application server mode, which may be one of $sync or
$async (defaults to $sync).
Deployment
235

The $variant entry optionally specifies a variant server. When present, it should point
to a map. The value denoted by the $portPlus key in this map is added to the server
port to produce another port, on which a variant server is deployed. The map may
also contain an $async and a $secure entry (both being booleans) for overriding the
main server settings. In our example, this causes two servers to be deployed: a
synchronous SSL server on port 443 and an asynchronous non-SSL server on port
543.

The $partition entry optionally specifies partitions for use by native or Flash clients.
See Section 7.4 for details.

The $session entry optionally specifies a timeout period and maximum error limit for
server sessions. The timeout should be expressed in seconds. If not specified (or zero
or negative), server sessions will not timeout. Session timeout is a practical means of
removing rogue sessions, which may have got into trouble due to internal error,
communication error, or infinite loops. If you use session timeout then you should
also use a timer in clients to ping the server to avoid healthy sessions timing out.
Sessions that throw more runtime errors than the specified limit are terminated.

The $log entry optionally limits the age of a log file. When a log file becomes older
than the specified limit, a new one is opened. The naming of the new file is dependent
on how the log file has been specified (see –logfile runtime option in Section 7.1.1).

The $zip entry optionally specifies compression requirements. When specified, all
messages and files exchanged between clients and server sessions are compressed,
provided they exceed the minimum size denoted by $min (in bytes).

The $load entry optionally specifies a file to be loaded by the server as soon as the
configuration file has been processed.

The $database entry specifies database connections. Each database is specified using
a key (e.g., $mysqlDb) which you can use in your code to depict a connection. For each
database, you can specify the following (see Chapter 12 for more detail on each
option).

$name provides an arbitrary descriptive name for the database.

$url specifies the URL of a specific database instance.

$user specifies a valid username for database login.

$password specifies a matching password for database login. This string may be
enclosed in square brackets to signify that it is an encrypted value.

$driver specifies a fully qualified JDBC driver class name for the database.

$timeout optionally specifies login and query timeout periods in seconds.

$schema optionally specifies a schema other than the default database schema.

$pool optionally specifies additional parameters for controlling the JDBC
connection pool.
236
JavaGram Agile Development
Where encrypted database passwords are used, a file containing a key must be created
and used to encrypt the passwords. This may be accomplished by running:
java -cp JAG.jar jag.misc.Cypher keyFileName
This program will attempt to read a key file at the specified location but in the initial
case, where a key file cannot be found, a new key file is created. The program then
prompts for password values that it will then encrypt.
7.2.2 Proxy Server
A proxy server is an intermediate server that directs client traffic to application servers,
without affecting the traffic content. Proxy servers provide these benefits:

Failover. If an application server fails for any reason, the proxy server detects this
and directs the traffic to other functioning application servers instead.

Load balancing. The proxy server can distribute the traffic amongst application
servers according to load balancing parameters, thus ensuring that the application
servers are evenly utilized.

Added security. In a production environment, the proxy server can be placed in the
demilitarized zone (DMZ) and the application servers placed within the secure
network outside the DMZ. This protects the application servers from external attacks.
A single proxy server can represent multiple application servers. However, to avoid a
single point of failure, multiple proxy servers can be deployed in failover or load
balanced manner. In this case, the selection logic is either provided by the client
(JavaGram Application Booter has a feature to support this) or by a third-party load
balancing tool.
Proxy servers are deployed in a manner similar to application servers, except that the
lib/svr/ProxyServer.jag script is used instead of lib/svr/AppServer.jag.
For example:
java -cp JAG.jar jag.run.svr.Server -root "C:/JavaGram" -config ProxyConfig.jag host "localhost:444" "C:/JavaGram/lib/svr/ProxyServer.jag"
Deployment
237
A proxy server configuration file is similar to an application server configuration file, but
typically has fewer settings. Example:
[
$download => [$default => ["jag" => $private, "*" => $public]]
,$upload => [$default => ["*" => $public]]
,$security => [$keystore => "C:/JavaGram/ssl/JAG.jks" ,$password => "changeit"]
,$session => [$timeout => 300, $error => 10] // 5 minutes timeout
,$log => [$timeout => 86400]
// 1 day per log file
,$zip => [$min => 1024]
,$proxy =>
[$loadBalance
,[$host => "localhost", $port => 443, $weight => 1.0,
$keystore => "C:/JavaGram/ssl/JAG.jks"]
,[$host => "localhost", $port => 442, $weight => 0.5,
$keystore => "C:/JavaGram/ssl/JAG.jks"]
]
]
Note how the $proxy entry specifies the application servers for this proxy server. See
sys.proxyServer() in Chapter 10 for details of how an application server is specified in
this context.
7.2.3 Multi-tier Servers
In most cases, a client communicates with a single logical server, though the latter may
have multiple physical instances for failover or load balancing.
In general, however, a client can communicate with multiple logical servers, and these
servers may in turn be clients of other servers.
Every client has a default server, from which it’s booted and obtains its code. On the
client side, the stream for this server is denoted by sys.loader. When the client
communicates with this server, it doesn’t need to specify the server stream. However, for
other servers, the stream must be explicitly stated. Here is how the client specifies the
target server in a number of possible situations:

To open a stream connection to a server, the client should use the sys.client()
method.

To invoke a remote call on a non-default server, the client must use the targeted
remote call syntax (see Section 6.1.4).
238
JavaGram Agile Development

To download a script from a non-default server, the client must specify the source
server’s stream, by passing it to the relevant method such as sys.download(),
sys.use(), or sys.rload(). When using <load>, the server stream can be specified
using the source property.

To upload a file to a non-default server, the client must specify the target server’s
stream, by passing it to sys.upload().
7.2.4 Deploying a Server as a Service
On the Windows platform, it’s often desirable to install a server (especially a production
server) as a service, so that administrators can logout of the server machine without the
server stopping. To do this, you need the Java Service tool, which you can download
from http://forge.ow2.org/projects/javaservice/. This tool is open source and is provided
under the GNU Lesser General Public License (LGPL).
Please note that Java Service requires JDK installed on your machine – JRE is
insufficient.
7.3 Native Client Deployment
To deploy a native client, make sure that you have JRE and JAG installed on your
machine.
To run a client, define a command line shortcut using the options described in Section 7.1
based on whether it’s a GUI or non-GUI client. To make client deployment easier for
users, you may want to consider creating an installation executable that installs JAG.jar,
JAG.jks, and an appropriate command line shortcut that points to the desired server.
7.3.1 JavaGram Application Booter
When you run a JavaGram application using the –boot option, the following window is
presented to the user.
This window is presented as read-only when the –stage command line option is set to
prod, in which case, it serves only as confirmation that the application is being booted
from a remote server. As soon as the boot sequence is completed, the window is removed
and the application is displayed instead. The Main Script, Server Host and SSL Keystore
fields within this dialog are pre-populated from the corresponding command line options.
Deployment
239
If you want to give the user the opportunity to visually change these settings then don’t
set –stage to prod. After changing the settings, the user should press the Run button for
the boot sequence to begin.
The Main Script field can optionally specify a partition. For example, the value
CrmPart:crm/Main.jag will be treated as partition CrmPart and main script crm/Main.jag.
The partition is downloaded first, followed by a request for the script (which may also be
sourced from the partition, depending on its composition).
If a file named <root>/boot.jag exists then this file is used to lookup alternative hosts for
the Server Host field. This file should contain a map, against which the Server Host is
looked up. For example, if the file contains
[
"myhost:443" => [$failOver, "test1:443", "test2:443"],
"yourhost:443" => [$loadBalance, "test1:443", "test2:443"]
]
then if the Server Host field contains myhost:443, it’s replaced by three fail-over hosts:
myhost:443 test1:443 test2:443
and if it contains yourhost:443, it’s replaced by three load-balanced hosts:
yourhost:443+test1:443+test2:443
This method allows you to effectively change the –host runtime value, without having to
amend the application shortcut installed on a user’s machine. The boot.jag file can be
downloaded to a user’s machine through a sys.use() call in the application code, and will
take effect the next time the user runs the application.
Some user sites route their traffic through a firewall proxy server. In such cases, to run a
JavaGram client application, correct proxy server settings must be provided. These can be
specified in the following fields:

The Proxy Host field should contain the host name (or IP address) and port number
for the proxy server, in format host:port.

If the proxy server also requires authentication then the Proxy Username and
Password fields should also be filled. Otherwise, these fields should be left blank.
These fields automatically default to the values stored in <root>/proxy.jag. Any proxy
server changes made by the user in the Booter window are saved back to this file. For
security, the password field is always stored in encrypted format.
240
JavaGram Agile Development
7.3.2 Automatic Upgrade
Once a JavaGram process loads a file, any future attempts to load it will be ignored,
unless the file has been modified since the last load. (When the latter happens on the
server side, the file is recompiled and the cache is updated.) An updated file will be
successfully re-loaded if the structure of the classes within it haven’t changed. In other
words, the changes must be limited to the implementation of methods.
Here are a couple of typical scenarios illustrating how code changes are automatically
distributed:

The server code base is in source format (.jag files). A source file on the server is
patched to fix a bug. The fix affects the logic of a method, but not its signature. When
a client (or server session) attempts to load the file, the file is compiled and the
server-side cache is updated. The client (or server session) obtains and reloads the file
successfully and hence the patch takes effect.

The server code base is in binary format (.jax files). A binary file on the server is
replaced by a patched version to fix a bug. The fix affects the logic of a method, but
not its signature. When a client (or server session) attempts to load the file, the file is
decompiled and recompiled to update the server-side cache. The client (or server
session) obtains and reloads the file successfully and hence the patch takes effect.
In summary, whether you patch a server in source or binary format, the patch is
automatically distributed. You don’t need to re-start the server.
The only other component that may potentially change and require distribution to clients
is JAG.jar. You can implement a simple scheme in your application that allows this file to
be also distributed to clients. The process is managed by the library script
lib/gui/Upgrade.jag.
To check for an upgrade of JAG.jar, instantiate the Upgrade class and override its
getRebootCmd() method, as illustrated below for the Quest sample application. Then
invoke the check() method and pass it the minimum required major and minor version of
JAG.jar (e.g., 1.3 in this example).
Upgrade upgrade = new Upgrade() {
protected string getRebootCmd (string jarPath) {
return $"javaw.exe -cp \"{jarPath}\" jag.gui.Gui -host \"{sys.host}\" -stage
{sys.stage} -root \"{sys.root}\" -ssl \"{sys.ssl}\" -boot quest/Quest ";
}
};
upgrade.check(1, 3, "jar/JAG.jar", "jar/JAG.jar", sys.loader);
This code snippet effectively states that this application relies on having at least JAG
version 1.3. The code snippet should appear as early as possible in the application, before
the application GUI is rendered.
Deployment
241
If the JAG used to run the client falls short of the minimum version, the following dialog
will appear, inviting the user to initiate a download of the JAG used by the server (which
presumably will meet the minimum required version, otherwise the server has been
deployed incorrectly).
If the user presses the Download button, the server-side JAG will be downloaded into the
client, the client will exit and automatically re-run with the new version. At this point, the
new JAG permanently replaces the old one.
7.4 Browser Client Deployment
Browser-based clients use the Flash version of JavaGram runtime (JAG.swf). This runtime
is not invoked directly but through HTML.
7.4.1 HTML Embedding
The minimum HTML required to embed JAG.swf is shown below.
<object width="600" height="400">
<param name="movie" value="JAG.swf">
<embed src="JAG.swf" width="100%" height="100%"/>
</object>
However, this approach is not recommended as it does not cater for issues such as
detecting the presence of the Flash Player, browser idiosyncrasies, passing the required
arguments to JAG.swf, etc. The easiest and safest way to embed JAG.swf in your HTML is
to use the JavaGram IDE (see Chapter 14). If you configure a browser client in JADE and
run it, it will produce the necessary HTML file automatically. You can then model your
HTML file based on the generated one.
Your JavaGram installation contains the necessary components (within the flash/ subdirectory) for deploying browser clients, as summarized in the following table.
flash/AppTemplate.html
Template HTML used by JADE to generate your
client HTML file
flash/AC_OETags.js
JavaScript used by HTML files
flash/crossdomain.xml
Flash cross-domain policy file
flash/JAG.swf
Flash version of JavaGram runtime
flash/playerProductInstall.swf Used by HTML files for managing the installation of
Flash Player, if not present
flash/history/
Contains files for managing browser history
242
JavaGram Agile Development
flash/html/
Client HTML files generated by JADE are placed in
this sub-directory
The particular file of interest is AppTemplate.html, which contains complete code for
HTML embedding of JAG.swf. Within this file, you’ll notice the following line (formatted
as multiple lines here for ease of reading):
"FlashVars",
"partAndScript=<<PartAndScript>>
&hostAndPort=<<HostAndPort>>
&transport=<<Transport>>
&warning=<<Warning>>
&verbose=<<Verbose>>
&stage=<<Stage>>"
This is how runtime arguments are passed to JAG.swf. The placeholders <<…>> are
replaced with valid values by JADE when it generates a HTML file. Here is an example
for the Quest sample application after substitution:
"FlashVars",
"partAndScript=QuestPart%3Aquest%2FQuest.jag
&hostAndPort=localhost%3A543
&transport=socket
&warning=false
&verbose=false
&stage=prod"
The values must be URL-encoded (www.w3schools.com/TAGS/ref_urlencode.asp) so, for
example, QuestPart:quest/Quest.jag becomes QuestPart%3Aquest%2FQuest.jag.
The following FlashVars are supported:

partAndScript denotes the initial partition and script, separated by a colon. Partitions
are explained in the next section.

hostAndPort denotes the target server host and port, separated by a colon.

transport denotes the means by which messages are transported between client and
server ends, and may be one of socket, http, or https.

warning may be set to true or false. When set to false, warning messages are
suppressed.

verbose may be set to true or false. When set to true, the client runs in verbose mode
and outputs all messages exchanged with the server to the log.

stage may be set to dev, test, or prod.
Deployment
243
7.4.2 Flash Security Sandbox
Flash security prevents an SWF file from accessing local resources. If an SWF file is
sourced from a domain then it’s allowed to access resources (e.g., images) from that
domain.
When you test a browser client in JADE, JAG.swf is loaded from the local disk. When
your application attempts to load images from a partition (whose data is sourced from the
server), Flash security may block this by (incorrectly) assuming that you’re attempting to
load an image from the local disk. You can work around this issue by placing JAG.swf in
the local-trusted sandbox. To do so, create the following directory if it doesn’t exist:
C:\Windows\System32\Macromed\Flash\FlashPlayerTrust\
The above path is for Windows7. The actual path may be different on your OS. Create a
file named PragSoft.cfg and add one line to the file for each SWF file you want to add to
the local-trusted sandbox. For example:
C:\JavaGram\flash\JAG.swf
7.4.3 Partitions
Because of the Flash asynchronous behavior and security restrictions, JAG.swf uses a
different procedure to obtain JavaGram code from the server. As we saw earlier, JAG.jar
downloads compiled JavaGram scripts from the application server and caches them on
the client side. JAG.swf cannot do this because Flash security prevents it from accessing
the local disk storage. Instead, it uses partitions.
A partition is a server-generated group of pre-processed files, packed in ZIP file. The
makeup of each partition is specified in the server configuration file (under the
$partition key). You can include any type of file in a partition (JavaGram scripts, GIF
files, HTML files, etc). When the application server starts, it generates the partitions
specified in its configuration. Every JavaGram script included in a partition is validated
through compilation. The client-side version of the compiled file is then decompiled and
included in the partition. For security, no server-side code is ever included in a partition.
You can also use partitions for native clients. This will speed up the client because there
will be far fewer server calls to download individual files.
Consider the following partition entry in a server configuration file.
,$partition =>
[$cache => "parts"
,$exclude => [".svn/*", "*.jagzip", "report/generated/*.pdf"]
,"QuestPart" =>
[("quest/", $recursive, "*.jag", "*.html", "*.gif")
,("lib/", $recursive, "*.jag", "*.gif")
]
244
JavaGram Agile Development
]
The $cache key optionally denotes a server-side directory name for where the partitions
are deposited (defaults to parts if not specified).
The $exclude key optionally specifies file patterns to be excluded from all partitions. Use
this as a convenient means of excluding files that would otherwise match the partition
rules.
Any string key within the partition map (e.g., "QuestPart" above) is treated as a partition
name, and must denote a vector of file patterns. All file patterns are relative to the
server’s sys.root. The following file patterns are supported:

An explicit file path. For example, "crm/common/Utils.jag" matches exactly one file.

An explicit file path with wildcard for file name, but not any other part. For example,
"crm/login/bo/*.jag" matches all JAG files directly inside the nominated directory.

A directory pattern. For example, ("crm/login/gui/", "*.jag", "*.gif") matches all
JAG and GIF files directly inside the nominated directory, whereas
("crm/login/gui/", "*.*") matches all files directly inside the nominated directory.

A recursive directory pattern. For example, ("crm/login/gui/", $recursive,
"*.jag", "*.gif") matches all JAG and GIF files inside the nominated directory and
its sub-directories recursively, whereas ("crm/login/gui/", $recursive, "*.*")
matches all files inside the nominated directory and its sub-directories recursively.

A partition/file exclusion pattern. For example, ($exclude, "ReportPart",
"crm/db/Gen.jag") causes every file within the ReportPart partition as well as the file
"crm/db/Gen.jag" to be excluded from the current partition. The list can contain
multiple partition names and/or file paths to be excluded. No wild-card patterns are
allowed.
A Flash client requires at least one partition (called the initial partition) which is specified
via the partAndScript parameter described earlier. The client may load additional
partitions using the sys.partition() method (see Chapter 10). Received partitions are
unpacked by the Flash client and their content kept in memory for later reference.
When a Flash client attempts to load a script or use a file, it looks for it in the loaded
partitions. If the file is not present in any of the loaded partitions then it’s flagged as an
error.
You can also use partitions for a Java client. This is useful if you want to improve
performance by reducing the number of times the client contacts the server for the files it
requires. All relatively loaded files and files requested via sys.use() are sourced from
partition(s) if present in at least one partition. Files requested via sys.download() are
never sourced from a partition.
Deployment
245
7.4.4 Deploying to a Web Server
Assuming that you use socket transport, to test a Flash client, you don’t need a web
server. JADE allows you to run a client directly. You can also go to the flash/html/
directory and open the client’s HTML file directly with a browser.
However, if you customize your client HTML file further and introduce things that
require access to a web server, then you should deploy your HTML file to a web server.
To deploy to a web server, copy your flash/ directory to the root of your web server.
You only need to do this once. Subsequently, a modified HTML file can be copied to
<WebServerRoot>/flash/html/. If your web server points to, say, the domain www.acme.com
and your client HTML file is called, MyApp.html, you can access it using this URL:
http://www.acme.com/flash/html/MyApp.html
You can also set your application’s client root in JADE to point to the root of the web
server so that generated HTML files are deposited there directly.
7.4.5 Server Deployment with BlazeDS
When you specify your Flash client transport as http of https, you must deploy your
application server in a servlet container (such as Tomcat). JavaGram uses Adobe’s
BlazeDS as the underlying implementation for HTTP/HTTPS communication, which
provides an efficient binary communication protocol called AMF. In this case, the servlet
container performs the session management, so you don’t need to boot an application
server (or, more accurately, you must not boot an application server).
BlazeDS is a free product from Adobe. To use it (for example in Tomcat), download its
WAR file (blazeds.war) and place it in Tomcat’s webapps/ directory. Tomcat will
automatically expand the WAR file and produce a webapps/blazeds/META-INF/ and
webapps/blazeds/WEB-INF/ directories. Alternatively, you can expand the WAR file
yourself using a ZIP program such as 7-zip. Rename the blazeds/ directory as JAGBlaze/.
The following directories are of particular interest:

webapps/JAGBlaze/WEB-INF/lib/ contains the BlazeDS JAR files. You must add any
JAR files required by your server (JAG.jar, JDBC derivers, etc.) to this directory.

webapps/JAGBlaze/WEB-INF/flex/ contains the XML configuration files for BlazeDS.
Open the flex/remoting-config.xml file and add the following destination.
<destination id="JagBlazeService" channels="my-amf">
<properties>
<source>jag.run.svr.Session</source>
<scope>session</scope>
</properties>
</destination>
246
JavaGram Agile Development
This destination defines the service that JavaGram uses for BlazeDS communication. The
flex/services-config.xml file defines the channel set for BlazeDS communication.
However, you don’t need to define the channels in this file, as JAG.swf creates the channel
set and channels dynamically from your nominated server address (as specified in the
HTML file).
For ease of deployment, it’s recommended that you package the above steps as a
deployment script that produces a ZIP file that you can simply expand for all the files to
go to the right places in your servlet container.
You also need to place a Flash cross domain policy file in the Tomcat root directory
(e.g., tomcat/webapps/ROOT/). For example:
<cross-domain-policy>
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-ports="*"/>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>
Finally, you must define an environment variable called JAGBLAZE_OPTIONS. For example:
-noWarn -root "C:/JavaGram/" -config acme/Config.jag lib/svr/BlazeServer.jag
where lib/svr/BlazeServer.jag is the BlazeDS adaptation of lib/svr/AppServer.jag.
Unlike normal JavaGram application servers (which boot as soon as the server is
deployed) this server will boot (causing partitions to be generated, etc.) when the first
session is created in response to a client connection. Therefore, the first client connection
will be slower than usual.
After making all your changes, you should restart Tomcat so that your changes take
effect.
7.5 Standalone Deployment
Standalone deployment is intended for applications that run independently. To make it
easier for users to install the release, you should package it as an executable installation
program that installs JAG, the application code, and any other necessary components
(e.g., database drivers).
If you want to protect the application’s intellectual property (IP), we recommend that you
release it in compiled rather than source format. To compile the application, either use
JADE’s build facility (see Section 14.2.6) or use the compilation command line explained
in Section 7.1.3.
To allow the user to upgrade to the latest version of the application, you can implement a
scheme whereby, upon start up, the application connects to a pre-nominated server,
checks for new releases, and gives the user the option to upgrade to it. It’s up to you how
Deployment
247
sophisticated you want this process to be. A minimal approach would just download the
release and ask the user to install it. A fully automated approach would perform the
download and install it automatically.
7.6 File Caching
JavaGram provides extensive file caching on both client and server sides to improve
runtime performance. The basic principle of file caching is to avoid processing or
transmitting a file unless it’s absolutely necessary.
7.6.1 Server-side Caching
All compiled files are cached by the server. The server will only recompile a script if its
source file has been modified since it was last compiled.
As a rule, a JavaGram server always compiles a script before making it available for
running on the client or the server side. For example, if a client attempts to access
Quest.jag or a server attempts to load Quest.jag, the server compiles Quest.jag to
produce two separate compiled versions of it – one targeted for the client and one for the
server. The compiled client version excludes all slocal methods and the implementation
of all remote methods. The compiled server version excludes all clocal methods and all
non-server code (such as GUI members). This ensures that neither side has access to code
that isn’t intended for it.
A compiled client side script that exceeds the zip threshold specified by the server
configuration is also compressed so that it’s ready for transmission to clients.
The server-side cache looks like this:
<root>/cache/client/script.jax
<root>/cache/client/script.jax.jagzip
<root>/cache/server/script.jax
// compiled for clients
// compiled and compressed for clients
// compiled for server
Note that compilation performed by a server is different from explicit compilation using
the –c command line option. The latter compiles a .jag file to produce an equivalent .jax
file. This is useful for packaging an application so that you don’t have to distribute its
source. Server compilation produces .jax files targeted at client and server sides, with
irrelevant information removed. This is for the internal caching mechanism of JavaGram
and not suitable for explicit packaging and distribution.
7.6.2 Client-side Caching
For efficiency, all client-side scripts are cached. The client will not download a file unless
its cached version mismatches the version on the server-side.
The client-side cache looks like this:
<root>/<relative-dir>/script.jax
248
JavaGram Agile Development
When the client attempts to load a script, a message is sent to the server which includes
the timestamp of the client-side cached file (if any). The server will only transmit the file
if the timestamp of the file it has in its cache mismatches that of the client’s. The client
will either receive this file (to update its own cache) and use it, or is told by the server to
use what it already has (because it’s up to date).
An exception to this rule is when the client discovers that its cached version of a script
has been encoded by an older version of the JAG codec. In this case, the client forces the
script to be downloaded.
A partition downloaded by a client is also cached (under <root>/parts/). Because a
server cases all the partitions to be regenerated, it will always cause subsequently run
clients to re-download requested partitions.
7.7 JavaGram Server Monitor
A deployed server requires some monitoring to ensure that it has enough resources to
support user demand, and to attend to any potential runtime issues as reported in the
server log. The JavaGram Server Monitor (JSM) is a utility written in JavaGram that
allows you to do this remotely, without having physical access to the server. This section
describes the monitor’s user interface and functionality.
7.7.1 Running JSM
JSM is part of the JavaGram release – you’ll find its source code under the monitor
directory. The procedure for running JSM is exactly the same as any other JavaGram
client application. For example:
javaw -cp C:/JavaGram/JAG.jar jag.gui.Gui -root C:/JagClient -ssl ssl/JAG.jks host localhost:443 -stage prod -noWarn -boot monitor/Monitor.jag
When you run JSM, it initially displays the following dialog.
Because JSM can connect to multiple servers and remembers the server list from a
previous run, you can use this dialog to manage the monitored server list before
monitoring commences. The servers are displayed in a table. You can add to the list by
pressing the Add Server button and directly editing the inserted row. To remove rows,
tick their ‘Remove details’ column and press the Remove Details button. When you’ve
Deployment
249
finished, press OK – the dialog will disappear and the monitor’s main window will
appear instead.
7.7.2 Server Tabs
JSM displays a tab for each monitored server, named according to the server’s host and
port. Within this tab, a number of sub-tabs appear that display information about that
server. This information is periodically updated.
The Sessions graph tab displays the number of live sessions and live threads.
The Sessions table tab displays a list of all live sessions. A set of buttons to the right of
the table allow you to act on the table.
250
JavaGram Agile Development
The Server Load tab displays a number of load indicators for the server, such as
database requests, server requests, and active server requests per second.
The Memory tab displays the maximum memory available to the server (as determined
by a Java runtime option), the total memory allocated, and memory actually used so far.
Deployment
251
The DB Pools tab displays a bar chart of the database connections utilized by the server.
The DB Queries tab shows details of the currently active database queries. This table is
empty unless there are queries currently in progress.
252
JavaGram Agile Development
The Server Log tab displays the contents of the server log file. This tab is empty unless
server output has been redirected to a file using the –logfile command line option.
The File Exchange tab provides a view of the file system for the client side (left) and
server side (right). You can navigate both file systems and download and upload
files/directories by selecting them and pressing the Download or Upload button. You can
also compare a file by selecting it from both sides and pressing the Diff button. This is
useful as a check before you upload a patch to a server.
Deployment
253
The Options button in the JSM toolbar displays a dialog for specifying your preferences.
The Add/Maintain Server button displays a dialog for maintaining the list of servers to be
monitored by JSM. This is the same as the initial dialog you see when you run JSM.
The Suspend button suspends the monitoring of the front-most server tab. When pressed,
the button changes to Resume which, when pressed again, will resume the monitoring.
The Remove Server button removes the front-most server tab and stops monitoring it.
There are also 6 buttons on the extreme right of the JSM window that act on the
monitored server(s).
The Compact button forces garbage collection of the front-most server tab.
The JConsole button displays the Java console for the front-most server tab.
254
JavaGram Agile Development
The Broadcast button displays a dialog box for broadcasting a message to users. The
scope of the broadcast can be all users on the front-most server tab or all users of all
servers. This is useful for informing users about significant events, such as bringing down
a server for maintenance.
The Reset Server button resets the front-most server tab, causing the server to reload its
configuration. No user sessions are affected. Use this if you’ve made changes to a server
configuration and you want it to take effect without restarting the server.
The Clear DB Pools causes a new database connection pool to be used for new
connections in the front-most server tab. Existing database connections won’t be affected.
The Evaler button displays a dialog for evaluating arbitrary expressions within the
context of the front-most server tab.
Type the expression to be evaluated in the top panel and press Evaluate. The result of
evaluation will be displayed in the bottom panel.
Deployment
255
8 Quest Sample Application
This chapter describes the development of a complete JavaGram application to further
elaborate the concepts described in earlier chapters. In order to fully grasp a new
technology, there is no real substitute for studying the development of a real-life
application. This is often the best way to see how techniques and concepts that were
earlier described using trivial examples are applied in more complex settings.
The application described in this chapter is a multi-user tool for managing project-related
issues. To provide you with a complete picture, we’ll describe all aspects of the
application, covering: requirements, user interface, class design, and code. After reading
through this chapter, we encourage you to experiment – extend the application, add new
features, or enhance the existing features. You’ll learn much more through such
experiments than simply reading.
Quest has been implemented such that it can be deployed in any of the deployments
models supported by JavaGram, including: standalone, synchronous client-server,
asynchronous client-server, and using a native or a browser-based client.
8.1 Introduction
Issues arise in almost every project, representing things that require attention. An issue
can be a defect (something that’s not working right), an enhancement (a request to
improve something), or a change request (a request to change an earlier requirement).
Systematic tracking of issues is essential if project risks are to be managed properly.
Because of the complexities involved, most projects use a software tool to at least
partially automate the process, thus reducing the risk of administrative errors.
8.1.1 Requirements
An issue tracking tool should allow project participants to do the following tasks.

Raise and submit a new issue.

Assign an issue to an individual, so that they can investigate and/or fix it.

Open an issue so that it can be worked on.

Resolve an issue.

Close an issue after it has been fixed and verified.

Reject an issue, if investigation reveals that it’s not really an issue.

Find out the status of an issue (its completion state, the person to whom it’s assigned,
when it was assigned, its resolution, etc.)

View the history of an issue (everything that has been done to the issue to date).
256
JavaGram Agile Development

Search for all raised issues using various search criteria.

Produce reports on issues.
To avoid uncontrolled usage, access to the tool should be limited to authorized users who
have been provided with a user account. A user account includes:

A username that uniquely identifies the user, as well as a numeric user ID which does
the same, but is internally used to keep track of who has done what.

A password that allows the user to login to the system.
Additionally, the tool should provide administrative functions, including:

Ability to create new user accounts.

Ability to specify access rights for users (i.e., what they can or can’t do).

Ability to enable, disable, and delete user accounts.

Ability to change user passwords. Additionally, each user must be able to change
their own password.
Finally, it’s important that attempts by multiple users to concurrently work on the same
data be handled in a consistent and predictable fashion:

Multiple users should be able to concurrently view the same issue.

Only one user at a time should be able to modify an issue.

Only the latest version of an issue should be available for modification.
8.1.1.1 Issue Life Cycle
Each issue must follow a defined life cycle, as specified by the following state transition
diagram.
Quest Sample Application
257
In this diagram, each box represents a state, and each label on a directed line represents
an action which, when performed, causes a transition to another state. The initial state for
an issue is always New (i.e., all new issues are initially in this state). The final state for an
issue is Closed.
When an issue is in a given state, the permissible actions that can be performed on it are
denoted by the arrow labels emanating from the state. For example, for an Opened issue,
the permissible actions are: Assign, Resolve, and Reject.
Quest must enforce these rules:

The user must not be able to perform an action on an issue, other than the permissible
ones (e.g., the user must not be able to close an assigned issue).

Only the administrative user should be able to reopen a closed issue. This is reserved
for when an issue is closed prematurely, and needs to be reopened.

All actions and state changes must be recorded in the issue’s history.
Additionally, the above state transition diagram must be soft-coded so that it can be
easily changed.
8.1.1.2 Issue BO
The application must be able to store the following information for each issue Business
Object (BO):

ID. Each issue is identified by a unique numeric ID. This ID is allocated by the
database and is read-only.

Title. Provides a descriptive summary for the issue.

Project. Denotes the project to which the issue applies.

Module. Denotes the project module to which the issue applies.

Build. Denotes the build to which the issue applies.

Type. Identifies the type of an issue as one of: Defect, Enhancement, or Change
Request.

State. Represents the current state of the issue, as defined by its life cycle.

Severity. Identifies the issue severity as one of: Critical, High, Medium, or Low.

Priority. Identifies the issue priority as one of: Critical, High, Medium, or Low.

Raised By. Is automatically set to the user ID who originally created the issue.

Raised On. Is automatically set to when the issue was created.

Assigned To. Is automatically set to the user to whom the issue is assigned.

Description. Provides a full description of the issue.
258
JavaGram Agile Development

Resolution. Described how the issue has been resolved.
8.1.1.3 History BO
Each time an issue changes state, this change must be recorded using a History BO,
which contains the following information:

Actioned By. Identifies the user who caused the state change.

Action. Identifies the action that caused the state change.

Date. Specifies the date on which the action was performed

Old State. Identifies the previous state of the issue.

New State. Identifies the new state of the issue.

Note. The description entered by the user (if any) when performing the action
8.1.1.4 User BO
The application must be able to store the following information for each user:

ID. Each user is internally identified by a unique numeric ID.

Username. Each user is externally identified by a unique name.

Last Name. Specifies the user’s real last name.

Last Name. Specifies the user’s real first name.

Password. The password for the user account.

Note. Any additional information pertaining to the user.

Active. Boolean flag indicating whether the user is currently active. Only active users
can login to the system.

Can Unlock. Boolean flag indicating whether the user can unlock locked objects.

Can Delete. Boolean flag indicating whether the user can delete objects.
When you install Quest, a special user account (Username=admin) is pre-created. This
user has special administrative rights, including the ability to create and delete user
accounts. However, the admin account itself can’t be deleted.
8.1.1.5 Attachment BO
The application allows documents (e.g., screen shots, test results) to be attached to issues
to serve as supporting documentation. An attachment captures the following information:

ID. Each attachment is internally identified by a unique numeric ID.

Issue ID. The ID of the issue to which this document is attached.

Name. Actual document name.

Size. The document size in bytes.
Quest Sample Application
259

Modified. The document’s last modification timestamp.
8.1.1.6 Rules BO
The Rules BO captures the rules governing the issue life cycle described earlier. By
capturing this information in a BO rather than hard-coding it, we make it possible for the
rules to be modified from a central location.
8.1.2 User Interface
The user interface for Quest consists of a set of windows. The user is first presented with
a Login window. After successful login, a main window is displayed which remains
active until the application is closed. The main window contains a number of panels and
can display other dialogs, depending on the task being performed by the user. The
following hierarchy summarizes all the user interface elements.


Login Window
Main Window
 Menu bar
 Toolbar
 Navigation Tree
 Content Pane
 User Search Panel
 User Details Panel
 Change Password Dialog
 Issue Search Panel
 Issue Details Panel
 Description Tab
 Resolution Tab
 Attachment Tab
 History Tab
 Assign Dialog
 Resolve Dialog
 Reject Dialog
 HTML Panel (for help and reports)
 Log Panel
 Pick Lists Panel
8.1.2.1 Login Window
The Login window is the first window displayed when the application is run. It invites
you to provide your username and password to be granted access.
260
JavaGram Agile Development
For security, the password field will echo a * for each typed character. Invalid username
and password combinations are rejected. Pressing Cancel will abort the login and quit the
application.
8.1.2.2 Main Window
The main window is displayed immediately after successful login.
The window title bar indicates the mode in which the application is run. The title may
contain one of:

Standalone. This means that the application is entirely running on the user’s
computer. The database server, however, may be on a different host.

Host IP address and port. This means that the application is running in client-server
mode, allowing multiple users (clients) concurrent access to the same server. The
displayed host address refers to the host on which the server runs, and the port refers
to the port on which the server listens for requests.
Quest Sample Application
261
The main window contains a navigation tree on the left side. When you select a node in
this tree, the content pane on the right side displays the information content of that node.
For example, in the above figure, the Help node is currently selected, causing the main
help page to be displayed.
A split pane separates the navigation tree and content pane, so that the vertical border
between the two can be dragged sideways.
8.1.2.3 Menubar and Toolbar
The menubar in the main window contains a set of pull down menus, which provide
commands for performing tasks. For example, selecting the Exit command from the File
menu will close the application.
The toolbar provides a set of buttons for commonly used commands. When you select a
node in the tree, these buttons are enabled/disabled to visually indicate what you can do
with that node. These buttons are:

Save. This button is enabled when a modified node in the tree is selected. A modified
node always has a * next to it, to indicate that it requires saving. Pressing Save causes
the data for the node to be saved to the database.

Save All. Same as Save except that it saves all unsaved nodes in the tree.

Preview. This button is enabled when a help node or report node is selected. Pressing
it causes the HTML page for the node to be displayed in a separate browser window.

Hide. This button is enabled when an issue, user, or report node is selected in the tree.
Pressing it cases that node to be removed (not deleted) from the tree.

Unlock. This button is enabled when a modified issue or user node is selected in the
tree. Pressing it causes the issue or user object to be unlocked. The user will be
prompted to confirm the operation. Only users who have been given ‘unlock’
permission can perform this task.

Delete. This button is enabled when an issue, user, or report node is selected in the
tree. Pressing it cases that node and its data to be permanently deleted. The user is
prompted to confirm the operation.

Toggle Tree. This button hides the navigation tree to maximize the available space
for the content pane.

Back. This button causes the previous node (in the navigation chain) to be selected
instead.

Forward. This button causes the next node (in the navigation chain) to be selected
instead.

Refresh. This button refreshes the current content pane by re-fetching its data from
the database.

Report. This button is enabled when a reportable node is selected. It generates a
HTML report that contains the node data, and adds it as a child of the Reports node.
262
JavaGram Agile Development

XML Data. This button is enabled when a user or issue node is selected. It displays
the node’s underlying data in XML format.

JAG Data. This button is enabled when a user or issue node is selected. It displays
the node’s underlying data as a JavaGram object literal.
8.1.2.4 User Admin Panel
This panel is displayed when the User Admin node is selected in the tree. It allows you to
search for users in the database.
To perform a search, enter the search criteria and press the Find button. To view the
details of a user, select the user row in the table and press the Details button, or just
double-click the row. The user is added to the tree, as a child of the User Admin node.
To create a new user account, press the New button. To delete a user, select its row and
press the Delete button. These two buttons are only enabled for the admin user.
8.1.2.5 User Details Panel
When a user is selected in the tree (e.g., after a search, or after creating a new user), the
content pane displays the user details as a panel.
Quest Sample Application
263
For non-admin users, this panel is displayed as read-only. The admin user can use this
panel to edit the details and/or access rights of a user, as well as changing the user’s
password via the Change Password button. Users can also change their own password by
choosing Change Password from the Tools menu. In both cases, the following dialog is
displayed.
Enter the old password, the new password, and confirm the new password. Press Change
to finalize the change. When the admin user is changing another user’s password, the Old
Password field is disabled and not required.
8.1.2.6 Issue Search Panel
This panel is displayed when the Issue Search node is selected in the tree. It allows you to
search for issues in the database.
264
JavaGram Agile Development
To search for an issue by ID, select ‘By ID’ from the Search combo and enter the issue
ID in the ID field.
To search by other criteria, select ‘By Criteria’ from the Search combo and enter your
search criteria into the fields provided. Any of these fields (or even all of them) can be
left blank. A blank field will match any record. If all the fields are left blank then the
search will return all issues in the database.
For the Title field, you can enter the first few character of a title. All issues that begin
with this prefix will match. The Module field follows the same rule. All searches are case
insensitive.
To perform a search, press the Find button. The matching issues are then displayed in the
table. The issues are initially sorted in ascending ID order. To sort by another column,
click on the column title. To sort in descending order, hold the shift key down while
clicking on the column title.
You can view the details of an issue by first selecting its row in the table and then
pressing the Details button. A convenient shortcut for this is to double click the row in
the table. The issue is then added as a node to the tree (as a child of the Issue Search
node), and selected so that its content is displayed in the content pane (see the next
section).
To clear the table, press the Clear button. This will remove all the issues from the table
(but will not remove them from the database). Any issues added to the tree are also
removed.
Quest Sample Application
265
You can use the Jump To field to quickly locate an issue by entering the first few
characters of the sort field. This is useful when the table contains a large number of
issues.
8.1.2.7 Issue Details Panel
When an issue is selected in the tree (e.g., after a search, or after creating a new issue),
the content pane displays the issue details as a panel, having four tab pages at the bottom
of the panel.
A set of buttons appear to the right of these tabs. These buttons allow you to perform
actions on the issue. Depending on the issue state, only the permissible action buttons are
enabled, as determined by the issue life cycle.
The Issue Details panel displays the general details of the issue. Fields that are
automatically controlled by Quest (such as ID and State) are disabled so that you can’t
change them. All other fields can be altered regardless of the issue state.
The Description tab allows you to enter a detailed description of the issue in free text
format. Similarly, the Resolution tab allows you to enter a detailed account of how the
issue has been fixed.
The Attachments tab lists all the supporting documents that have been attached to the
issue. To attach a document, press the Attach button, navigate to and select the desired
file, and press Open. To view an attachment, select its row and press View, or simply
double-click the row. To edit an attachment, select its row and press Edit – the
266
JavaGram Agile Development
attachment will be locked for editing. Once you’ve made your changes, close the
document, and press Save to save your changes, or press Unlock to discard your changes.
To delete an attachment, select its row and press Delete.
The History tab lists the actions performed on an issue, including the user who’s
performed the action, when the action was performed, the old and new state, and an
optional note.
When you assign an issue, the following dialog is displayed. Select the user to whom the
issue is to be assigned and press Assign.
When you resolve an issue, the following dialog is displayed. Describe how the issue was
resolved and press Resolve.
Quest Sample Application
267
Similarly, when you reject an issue, the following dialog is displayed. Describe why the
issue is being rejected and press Reject.
8.1.2.8 Generating Reports
When viewing a user/issue search panel or a user/issue details panel, the Generate Report
button in the toolbar (and the same in the Tools menu) is enabled. Pressing this button
causes an HTML report to be generated and added to the tree as a child of the Reports
node. Here is an example of a report generated for an issue.
268
JavaGram Agile Development
8.1.2.9 Log Panel
When you select the Settings/Log node, the content pane displays the application log,
where all application output is directed. If you instrument Quest by adding output
statements, all such output will appear in this panel.
8.1.2.10
Pick Lists Panel
When you select the Settings/Pick Lists node, the content pane displays all customizable
pick lists for the application.
Quest Sample Application
269
To view/edit the details of a pick list, select its row and press Details, or simply doubleclick the row. The following dialog appears.
Make your changes using the buttons to right of the dialog and press OK to save your
changes. Pick list changes are immediately reflected in the application’s combo boxes.
8.1.2.11
Help Panel
When you select a help node (or report node in the tree), its content is displayed as a
HTML page in the content pane. To open the HTML page in an external web browser,
select the node and press the Print Preview in the toolbar, or choose the same from the
File menu, or simply double-click the node.
8.2 Design
8.2.1 Object Model
8.2.1.1 quest/
The application scripts are located within the quest directory. The main class is Quest
which subclasses GuiApp. The rest of the application appears within the quest/bom and
quest/gui subdirectories.
270
JavaGram Agile Development
8.2.1.2 quest/gui/
This directory defines the main classes that comprise the application user interface.
AppPanel implements the overall application window, which includes the navigation tree
realized by AppTree. The user interface for all commands (toolbar buttons and menu
items) is realized by the Commands class.
Quest Sample Application
271
8.2.1.3 quest/bom/
This directory defines the BOs for the application. User, Issue, and Attachment are
defined as UserLockableObject BOs. History is defined as an Object BO. For each BO,
the attributes are captured in a separate class that the BO derives from. This enables the
details of a BO to be passed to the BO constructor as one argument.
Rules and DbTables are not persistent BOs. The former captures the definition of the issue
life cycle. The latter sets up the database tables for all persistent BOs.
272
JavaGram Agile Development
8.2.1.4 quest/gui/user/
This directory defines the user interface for managing user accounts. It includes the
UserSearch class for searching purposes, the UserScreen class for viewing and editing the
details of a user, and the PasswordDialog class for changing user account passwords.
Quest Sample Application
273
8.2.1.5 quest/gui/issue/
This directory defines the user interface for managing issues. It includes the IssueSearch
class for searching issues, the IssueScreen class for viewing and editing the details of an
issue, and a number of dialog classes that support the actions that can be performed on
issues.
274
JavaGram Agile Development
8.2.1.6 quest/gui/misc/
This directory defines the HtmlPanel which is used to display the HTML content of
reports and help pages.
8.2.1.7 quest/gui/setting/
This directory defines the PickListPanel class which displays the application pick lists,
and the LogPanel class which displays the application log.
Quest Sample Application
275
8.2.2 Data Model
The Quest data model is very simple. It consists of four tables that provide persistence for
BOs of the same name.
Each table has an integer primary key called id which uniquely identifies rows within
that table. The rest column of each table is used to capture class attributes that are not
explicitly defined within the table. The tables for lockable objects also have a locked_by
column, as required by the UserLockableObject class.
8.3 Implementation
The code for Quest is presented below, divided into logical categories. In the interest of
brevity and given the highly readable nature of the code, no detailed commentary is
provided.
8.3.1 Business Objects
When reading the BOs code, pay particular attention to methods intended for the server
side (i.e., those defined as remote or slocal). The distinction conveys the intended
distribution model of the application code.
8.3.1.1 User BO
<jag domain="quest/bom">
<load>
"lib/bom/UserLockableObject"
276
JavaGram Agile Development
"lib/bom/Object"
"lib/lang/Common"
</load>
class UserDetails {
int id;
// Internal unique numeric user ID
getable string username;
// External unique username
getable string password;
// Encrypted password
getable string firstName; // User's real first name
getable string lastName;
// User's real last name
getable string email;
// User's email address
getable boolean active;
// When true, user is active
getable boolean canUnlock; // When true, user can unlock objects
getable boolean canDelete; // When true, user can delete objects
getable string note;
// Note about the user
}
class User extends UserDetails, UserLockableObject {
static final int MIN_USERNAME_LEN = 4;
static final int MIN_PASSWORD_LEN = 4;
static final string ENCRYPTION_KEY = "quest";
static final string ADMIN_USER_NAME = "admin";
protected static vector<map> allUsersInfo;
protected static vector<map> allActiveUsersInfo;
static {
defineTable(User,
[ $db => $questDb
, $table => $user
, $columns =>
[ $id => $id
, $user_name => $username
, $first_name => $firstName
, $last_name => $lastName
, $active => $active
, $can_unlock => $canUnlock
, $can_delete => $canDelete
, $rest => [$fields => [$password, $email, $note] , $compress => false]
]
, $lock => [$column=>$locked_by, $style=>$byUser]
]
);
}
public User (UserDetails details) {
sys.assign(this, details, UserDetails);
validateUsername();
validatePassword(password);
password = encryptPassword(password);
}
protected void validateUsername () {
if (sys.length(username) < MIN_USERNAME_LEN)
throw new Exception($"username must be at least {MIN_USERNAME_LEN} chars long!");
}
protected slocal void validateUniqueUsername () {
validateUsername();
int theId = usernameToId(username);
if (theId >= 0 && id != theId)
throw new Exception($"username '{username}' is not unique!");
Quest Sample Application
277
}
public void validatePassword (string password) {
if (sys.length(password) < MIN_PASSWORD_LEN)
throw new Exception($"password must be at least {MIN_PASSWORD_LEN} chars long!");
}
private remote static int usernameToId (string username) {
query ($questDb) {
native rs = _usernameToId(username);
if (sql.next(rs))
return sql.get@int(rs, 1);
}
return -1;
}
private static string encryptPassword (string password) {
return sys.cypher@string(password, ENCRYPTION_KEY);
}
public static User login (string username, string password, Callback cb = null) {
User user @= Object.findOne(User, $username, username)
?> cb != null
-> {
if (!validate(user, password)) {
Exception e = new Exception("invalid username or password");
if (cb == null)
throw e;
else
cb.failed(e);
} else
cb?.completed(user);
} -> cb?.failed();
return user;
}
private static boolean validate (User user, string password) {
return user != null && encryptPassword(password) == user.password;
}
public void logout () {
}
public string title () {
return username;
}
public boolean checkPassword (string password) {
return this.password == encryptPassword(password);
}
public void changePassword (string password, Callback cb = null) {
validatePassword(password);
this.password = encryptPassword(password);
persist() ?? cb != null -> cb.completed(null) -> cb.failed();
}
public boolean isAdminUser () {
return username == ADMIN_USER_NAME;
}
public vague getId () {
// Override Object.getId() for efficiency:
return id;
}
public static vector<User> find (map criteria, Callback cb = null) {
map like = map();
278
JavaGram Agile Development
map exact = map();
if (nonEmpty(criteria.$username))
like[$username] = criteria.$username@string + "%";
if (nonEmpty(criteria.$firstName))
like[$firstName] = criteria.$firstName@string + "%";
if (nonEmpty(criteria.$lastName))
like[$lastName] = criteria.$lastName@string + "%";
if (nonEmpty(criteria.$active))
exact[$active] = criteria.$active == "Yes";
if (nonEmpty(criteria.$canUnlock))
exact[$canUnlock] = criteria.$canUnlock == "Yes";
if (nonEmpty(criteria.$canDelete))
exact[$canDelete] = criteria.$canDelete == "Yes";
criteria = map($exact => exact, $like => like);
vector<User> matches @= Object.find(User, criteria)
?? cb != null
-> cb.completed(matches)
-> cb.failed();
return matches;
}
public static User findById (vague id, Callback cb = null) {
User user @= Object.findOne(User, $id, id)
?? cb != null
-> cb.completed(user == null ? vector() : vector(user))
-> cb.failed();
return user;
}
public remote void persist () {
validateUniqueUsername();
[email protected]();
}
public static vague userModel (boolean active, symbol cmd, int idx) {
switch (cmd) {
case $count: return sys.length(getAllUsersInfo(active));
case $get: return getAllUsersInfo(active)[idx][$name];
case $id: return getAllUsersInfo(active)[idx][$id];
}
return null;
}
public static map getUserInfo (boolean active, int id) {
for (map m in getAllUsersInfo(active)) {
if (m[$id] == id)
return m;
}
return [=>];
}
public static void initAllUsersInfo () {
getAllUsersInfo(false);
getAllUsersInfo(true);
}
protected static vector<map> getAllUsersInfo (boolean active) {
if (active) {
if (allActiveUsersInfo == null) {
allActiveUsersInfo @= []; // Avoid async re-entry
allActiveUsersInfo = _getAllUsersInfo(active) ?? sys.async -> null;
}
Quest Sample Application
279
return allActiveUsersInfo;
}
if (allUsersInfo == null) {
allUsersInfo @= [];
// Avoid async re-entry
allUsersInfo = _getAllUsersInfo(active) ?? sys.async -> null;
}
return allUsersInfo;
}
protected remote static vector<map> _getAllUsersInfo (boolean active) {
// Always add an empty user to the list:
vector<map> users @= vector(map($id=>0, $name=>""));
query ($questDb) {
native rs = _allUsersInfo(active);
while (sql.next(rs)) {
map info = sql.getRow@map(rs);
info[$name] = info[$firstName] == ""
? info[$lastName]
: info[$firstName]@string + " " + info[$lastName]@string;
sys.append(users, info);
}
}
sys.sort(users, true, [$name]);
return users;
}
public slocal static <text.sql.update int createTable () db=$questDb>
create table if not exists user (
id integer not null auto_increment,
user_name varchar(16) not null,
first_name varchar(16),
last_name varchar(16),
active bit,
can_unlock bit,
can_delete bit,
rest text,
locked_by integer,
primary key (id), index (user_name)
)
</text.sql>
protected slocal static <text.sql.query native _usernameToId (string username) db=$questDb>
select id from user where user_name={`username}
</text.sql>
protected slocal static <text.sql.query native _allUsersInfo (boolean active) db=$questDb>
select id, first_name, last_name from user {= active ? "where active=1" : ""}
</text.sql>
}
</jag>
8.3.1.2 Issue BO
<jag domain="quest/bom">
<load>
"lib/lang/Common"
"lib/bom/UserLockableObject"
"lib/bom/Object"
"quest/bom/User"
"quest/bom/Attachment"
"quest/bom/History"
280
JavaGram Agile Development
</load>
class IssueDetails {
int id;
setable string title;
getable string project;
getable string module;
getable string build;
getable string type;
setable string state;
getable string severity;
getable string priority;
getable int raisedBy;
getable date raisedOn;
setable int assignedTo;
getable string description;
getable string resolution;
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
Internal unique numeric issue ID
Issue title
Project to which it applies
Module to which it applies
Build to which it applies
Issue type
Issue state
Issue severity
Issue priority
ID of the use who raised it
Date on which it was raised
ID of the user to whom it's assigned
Issue description
Issue resolution
class Issue extends IssueDetails, UserLockableObject {
getable string raisedByName;
// Not persistent
getable string assignedToName; // Not persistent
protected setable vector<Attachment> attach;
protected getable vector<History> history;
static {
defineTable(Issue,
[ $db => $questDb
, $table => $issue
, $columns =>
[ $id => $id
, $title => $title
, $project => $project
, $module => $module
, $build => $build
, $type => $type
, $state => $state
, $severity => $severity
, $priority => $priority
, $raised_by => $raisedBy
, $assigned_to => $assignedTo
, $rest => [$fields => [$raisedOn, $description, $resolution], $compress => false]
]
, $lock => [$column=>$locked_by, $style=>$byUser]
]
);
}
public Issue (IssueDetails details) {
sys.assign(this, details, IssueDetails);
}
public string title () {
return sys.length(this.title) > 16
? sys.subStr(this.title, 0, 16) + "..."
: this.title;
}
public vague getId () {
return id;
}
Quest Sample Application
281
public static vector<Issue> find (map criteria, Callback cb = null) {
map like = map();
map exact = map();
if (nonEmpty(criteria.$title))
like[$title] = criteria.$title@string + "%";
if (nonEmpty(criteria.$module))
like[$module] = criteria.$module@string + "%";
if (nonEmpty(criteria.$build))
like[$build] = criteria.$build@string + "%";
if (nonEmpty(criteria.$project))
exact[$project] = criteria.$project@string;
if (nonEmpty(criteria.$type))
exact[$type] = criteria.$type;
if (nonEmpty(criteria.$state))
exact[$state] = criteria.$state;
if (nonEmpty(criteria.$severity))
exact[$severity] = criteria.$severity;
if (nonEmpty(criteria.$priority))
exact[$priority] = criteria.$priority;
if (nonEmpty(criteria.$raisedBy))
exact[$raisedBy] = criteria.$raisedBy;
criteria = map($exact => exact, $like => like);
vector<Issue> matches @= Object.find(Issue, criteria)
?> cb != null
-> {
for (Issue issue in matches)
issue.updateVolatiles();
cb?.completed(matches);
} -> cb?.failed();
return matches;
}
public static Issue findById (vague id, Callback cb = null) {
Issue issue @= Object.findOne(Issue, $id, id)
?> cb != null
-> {
issue.updateVolatiles();
cb?.completed(issue == null ? null : vector(issue));
} -> cb?.failed();
return issue;
}
public remote void persist () {
transaction (getDbTag()) {
for (History hist in history) {
if (!hist.persisted())
bom.persist(hist);
}
bom.persist(this);
}
}
protected remote void _delete () {
transaction (getDbTag()) {
sql.execUpdate(deleteAttach());
sql.execUpdate(deleteHistory());
bom.delete(this);
}
}
282
JavaGram Agile Development
public Issue updateVolatiles () {
raisedByName = raisedBy == null
? "" : User.getUserInfo(false, raisedBy)[$name]@string;
assignedToName = assignedTo == null
? "" : User.getUserInfo(false, assignedTo)[$name]@string;
return this;
}
public void populateAttach (Callback cb = null) {
if (attach == null) {
attach = Attachment.find(id, !sys.async ? null : new Callback(cb) {
public void completed (vague data) {
attach @= data;
super.completed(data);
}
});
}
}
public void addHistory (History hist) {
if (history == null)
history @= vector();
sys.append(history, hist);
}
public void populateHistory (Callback cb = null) {
if (history == null) {
history = History.find(id, !sys.async ? null : new Callback(cb) {
public void completed (vague data) {
history @= data;
super.completed(data);
}
});
}
}
public slocal static <text.sql.update int createTable () db=$questDb>
create table if not exists issue (
id integer not null auto_increment,
title varchar(64) not null,
project varchar(32),
module varchar(32),
build varchar(16),
type varchar(16),
state varchar(16),
severity varchar(16),
priority varchar(16),
raised_by integer,
assigned_to integer,
rest text,
locked_by integer,
primary key (id), index (assigned_to)
)
</text.sql>
protected slocal <text.sql.prepare native deleteAttach () db=$questDb>
delete from attachment where issue_id={?id}
</text.sql.prepare>
protected slocal <text.sql.prepare native deleteHistory () db=$questDb>
delete from history where issue_id={?id}
</text.sql.prepare>
Quest Sample Application
283
}
</jag>
8.3.1.3 Attachment BO
<jag domain="quest/bom">
<load>
"lib/lang/Common"
"lib/bom/Object"
"lib/bom/UserLockableObject"
"quest/bom/User"
"quest/Quest"
</load>
class AttachmentDetails {
int id;
// Internal unique numeric attachment ID
getable int issueId;
// ID of the issue to which this attachment belongs
getable string name;
// Attachment name
setable int size;
// Attachment size (in bytes)
getable date modified;
// Last modification timestamp
}
class Attachment extends AttachmentDetails, UserLockableObject {
setable string path;
// Attachment path
static final string TEMP_DIR = "temp/";
static {
defineTable(Attachment,
[ $db => $questDb
, $table => $attachment
, $columns =>
[ $id => $id
, $issue_id => $issueId
, $rest => [$fields => [$name, $size, $modified]]
, $content => [$fields => $path, $viaFile => $binary, $extend => true]
]
, $lock => [$column => $locked_by, $style => $byUser]
]);
}
public Attachment (AttachmentDetails details, string path) {
sys.assign(this, details, AttachmentDetails);
this.path = path;
}
public vague getId () {
return id;
}
public static string recommendLocalDir (int issueId) {
return sys.pathConc(sys.root, "attach", "issue" + issueId + "/");
}
protected slocal string getViaFilePath (symbol dbColumn, symbol field, int rowNum) {
string dir = sys.pathConc(sys.root, TEMP_DIR);
if (!sys.pathExists(dir))
sys.createPath(dir);
return sys.pathConc(dir, "Attachment_" + rowNum + ".zip");
}
public static string genUniqueZipFileName (string name) {
return sys.normPath(name + "_" + sys.date()@int + ".zip");
}
public static string genUniqueTempFilePath () {
return sys.pathConc(TEMP_DIR, sys.normPath("Temp_" + sys.date()@int + ".dat"));
284
JavaGram Agile Development
}
public static string getPartsName (map<symbol, string> parts) {
return parts[$ext] == "" ? parts[$name] : (parts[$name] + "." + parts[$ext]);
}
public static vector<Attachment> find (int issueId, Callback cb = null) {
vector<Attachment> matches @= Object.find(Attachment, $issueId, issueId)
?> cb != null
-> {
sys.sort(matches, true, [$name]);
cb?.completed(matches);
} -> cb?.failed();
return matches;
}
public static Attachment findById (vague id, Callback cb = null) {
Attachment attach @= Object.findOne(Attachment, $id, id)
?? cb != null
-> cb.completed(attach)
-> cb.failed();
return attach;
}
public clocal void save (boolean existing, Callback cb = null) {
if (path == null)
throw new Exception("no file path specified for " + name);
if (!sys.pathExists(path))
throw new Exception(path + " doesn't exist");
date timestamp @= sys.pathProps(path)[$modified];
if (existing && timestamp == modified) {
// No need for async handling, as we're raising an exception immediately:
unlock(Quest.singleton.getUserId());
throw new Exception(path + " hasn't been modified");
}
map<symbol,string> parts = sys.pathParts(path);
string zipRelPath = sys.pathConc(TEMP_DIR, genUniqueZipFileName(parts[$name]));
string zipAbsPath = sys.pathConc(sys.root, zipRelPath);
sys.createPath(sys.pathConc(sys.root, TEMP_DIR));
sys.zip(zipAbsPath, parts[$dir], getPartsName(parts));
if (sys.loader != null) {
sys.upload(zipAbsPath, zipRelPath);
sys.deletePath(zipAbsPath);
}
_save(zipRelPath, true, Quest.singleton.getUserId()@int)
?? cb != null
-> cb.completed(null)
-> cb.failed();
}
public void saveUploaded (string uploadedPath, Callback cb = null) {
_save(uploadedPath, false, Quest.singleton.getUserId()@int)
?? cb != null
-> cb.completed(null)
-> cb.failed();
}
protected remote void _save (string docPath, boolean zipped, int byUserId) {
path = sys.pathConc(sys.root, docPath);
if (!zipped) {
string zipPath = path + ".zip";
map<symbol,string> parts = sys.pathParts(path);
Quest Sample Application
285
string fileName = getPartsName(parts);
sys.zip(zipPath, parts[$dir], map(fileName => name), fileName);
path = zipPath;
}
int lockedBy @= lockedBy(false);
if (lockedBy != null && lockedBy != byUserId)
throw new Exception("Can't update file; object is locked by "
+ User.getUserInfo(false, byUserId)[$name]@string);
try {
// save all fields, including file to 'content' marked 'extend':
setExtendOn(true);
persist();
}
finally {
setExtendOn(false);
}
sys.deletePath(path);
path = null;
}
public string retrieve (string intoDir, Callback cb = null) {
string remotePath = _retrieve()
?> cb != null
-> {
if (sys.flash) {
sys.download(remotePath, name, null, false, $onDownload, list(cb));
} else {
sys.createPath(intoDir);
string localPath = remotePath;
if (sys.loader != null) {
localPath = sys.pathConc(intoDir, genUniqueZipFileName(name));
sys.download(remotePath, localPath, null, false);
}
sys.unzip(localPath, intoDir);
try {sys.deletePath(localPath);} catch (Exception e) {}
path = sys.pathConc(intoDir, name);
Util.setModifiedTimestamp(path, modified@int);
cb?.completed(path);
}
} -> cb?.failed();
return path;
}
protected void onDownload (vague res, Callback cb) {
if (res instanceof map) {
path @= res@map[$local];
cb.completed(path);
} else
cb.failed(res@Exception);
}
protected remote string _retrieve () {
try {
// get all fields, including file to 'content' column marked 'extend':
setExtendOn(true);
refresh();
}
finally {
286
JavaGram Agile Development
setExtendOn(false);
}
return path;
}
public slocal static <text.sql.update int createTable () db=$questDb>
create table if not exists attachment (
id integer not null auto_increment,
issue_id integer not null,
content mediumblob,
rest text,
locked_by integer,
primary key (id), index (issue_id)
)
</text.sql>
}
</jag>
8.3.1.4 History BO
<jag domain="quest/bom">
<load>
"lib/lang/Common"
"lib/bom/Object"
"quest/bom/User"
</load>
class HistoryDetails {
int id;
// Internal unique numeric history ID
int issueId;
// ID of the issue to which this applies
getable int actorId;
// ID of the user who acted
getable date actDate;
// Action date
getable symbol action;
// The action
getable string oldState;
// Issue's old state
getable string newState;
// Issue's new state
getable string note;
// Optional note
}
class History extends HistoryDetails, Object {
getable string actorName;
// Not persistent
static {
defineTable(History,
[ $db => $questDb
, $table => $history
, $columns =>
[ $id => $id
, $issue_id => $issueId
, $rest => [$fields => [$actorId, $actDate, $action, $oldState
,$newState, $note]
,$compress => false]
]
]
);
}
public History (HistoryDetails details) {
sys.assign(this, details, HistoryDetails);
}
public vague getId () {
return id;
}
Quest Sample Application
287
public string getFilteredNote () {
return note == null ? "" : note;
}
public static vector<History> find (int issueId, Callback cb = null) {
map exact = map($issueId => issueId);
vector<History> matches @= Object.find(History, map($exact => exact))
?> cb != null
-> {
afterFind(matches);
cb?.completed(matches);
} -> cb?.failed();
return matches;
}
private static void afterFind (vector<History> matches) {
for (History hist in matches)
hist.updateVolatiles();
sys.sort(matches, true, [$actDate]);
}
public History updateVolatiles () {
actorName = actorId == null
? "" : User.getUserInfo(false, actorId)[$name]@string;
return this;
}
public slocal static <text.sql.update int createTable () db=$questDb>
create table if not exists history (
id integer not null auto_increment,
issue_id integer not null,
rest text,
primary key (id), index (issue_id)
)
</text.sql>
}
</jag>
8.3.1.5 Rule BO
<jag domain="quest/bom">
// Defines the life-cycle of issues.
singleton class Rules {
static final map<string, vector<symbol>> STATE_TO_ACTIONS @= [
"New" => [$submit]
, "Submitted" => [$assign, $open]
, "Assigned" => [$open, $assign, $close]
, "Opened" => [$resolve, $reject, $assign]
, "Resolved" => [$close, $assign]
, "Rejected" => [$close, $assign]
, "Closed" => []
];
final vector<string> STATES @= [""] + sys.mapKeys(STATE_TO_ACTIONS);
public vector<symbol> stateToActions (string state) {
return STATE_TO_ACTIONS[state];
}
public string actionToState (symbol action) {
switch (action) {
case $submit: return "Submitted";
288
JavaGram Agile Development
case $assign: return "Assigned";
case $open: return "Opened";
case $resolve: return "Resolved";
case $reject: return "Rejected";
case $close: return "Closed";
}
return null;
}
public final vector<string> getStates () {
return STATES;
}
}
</jag>
8.3.1.6 DbTables
This script is not a BO, but creates the Quest database and its tables.
<jag domain="quest/bom">
<load>
"lib/bom/Object"
"lib/bom/PickList"
"quest/bom/User"
"quest/bom/Issue"
"quest/bom/History"
"quest/bom/Attachment"
</load>
// This class drops, creates, and populates the tables for Quest.
class DbTables {
public static void main () {
createDatabase();
transaction ($questDb) {
Object.dropTable(User, $questDb);
User.createTable();
}
UserDetails ud = [@UserDetails username=>"admin", password=>"admin"
, firstName=>"Admin", lastName=>"Administrator"
, active=>true, canUnlock=>true, canDelete=>true];
new User(ud).persist();
ud = [@UserDetails username=>"fred", password=>"fred", firstName=>"Fred"
,lastName=>"Smith", active=>true, canUnlock=>true, canDelete=>false];
new User(ud).persist();
ud = [@UserDetails username=>"jane", password=>"jane", firstName=>"Jane"
,lastName=>"Smith", active=>true, canUnlock=>true, canDelete=>false];
new User(ud).persist();
transaction ($questDb) {
Object.dropTable(Issue, $questDb);
Issue.createTable();
}
transaction ($questDb) {
Object.dropTable(History, $questDb);
History.createTable();
}
transaction ($questDb) {
Object.dropTable(Attachment, $questDb);
Attachment.createTable();
Quest Sample Application
289
}
transaction ($questDb) {
Object.dropTable(PickList, $questDb);
PickList.createTable();
}
PickListDetails pl;
pl = [@PickListDetails name=>"Type", description=>"Issue types", editable=>true
, items=>["", "Defect", "Enhancement", "Change Request"]];
new PickList(pl).persist();
pl = [@PickListDetails name=>"Severity", description=>"Issue severity"
, editable=>true, items=>["", "Critical", "High", "Medium", "Low"]];
new PickList(pl).persist();
pl = [@PickListDetails name=>"Priority", description=>"Issue priority"
, editable=>true, items=>["", "High", "Medium", "Low"]];
new PickList(pl).persist();
pl = [@PickListDetails name=>"Project", description=>"List of all projects"
, editable=>true, items=>["", "Default"]];
new PickList(pl).persist();
}
protected static void createDatabase () {
transaction ($mysqlDb) {
sql.execUpdate($mysqlDb, "create database if not exists quest");
}
}
}
</jag>
8.3.2 Main Window
A number of the user interface screens use the Hot plate to define their fields. This is
defined in the Screen library class. Its main purpose is to ensure that when the user edits
such a field, the screen is informed of the fact that it has been modified.
8.3.2.1 Quest
This class initially sets up the application main frame as a login window. As soon as the
user logs in successfully, the frame is redefined by the loginWorder() method, which
removes the login panel and instead adds the panel provided by the AppPanel class. Note
how we do this dynamically using the Util.getSingleton() method, thus ensuring that
the application panel is neither downloaded nor created until necessary.
<jag domain="quest">
//
//
//
//
//
//
//
//
//
//
//
Quest Sample JavaGram Application
Copyright (c) 2010 PragSoft Corporation. All rights reserved.
This application is provided for educational purposes. Use it as a 'best practice' guide for
JavaGram programming.
Quest also illustrates how the same JavaGram code can run under different deployment models:
1. As a standalone desktop application.
2. As a synchronous desktop client running against a synchronous JAG app server, directly or
through a JAG proxy server.
3. As an asynchronous desktop client running against an asynchronous JAG app server, directly or
through a JAG proxy server.
4. As an asynchrnous browser-based client running against an asynchronous JAG app server,
290
JavaGram Agile Development
//
directly or through a web server.
<load>
"lib/lang/Common"
"lib/gui/GuiApp"
"lib/gui/Upgrade"
"quest/bom/User"
"quest/gui/setting/LogPanel"
</load>
singleton class Quest extends GuiApp {
protected User currUser;
protected symbol loggedIn = $no;
public static final string REPORTS_DIR = sys.pathConc(sys.root, "quest/reports/");
public static final <Icon HAMMER image={sys.use("lib/gifs/Hammer.gif")}/>
<Worker login worker=loginWorker/>
<App app lookAndFeel=$windows stdOut={LogPanel.singleton.getTextArea()}
stdErr={LogPanel.singleton.getTextArea()}>
<Frame frame sizeable=false title="Quest Login" width=250 height=140
image={sys.use("lib/gifs/Planet.gif")} event=frameHandler>
<Panel panel>
<Layout.border/>
<Panel lay=$center>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 weight=0.0 align=$east>
<Label title="Username"/>
</Lay>
<Lay row=0 col=1 margin=2 fill=$horizontal>
<Field.text username focus=true event=fieldHandler
value={sys.stage == $prod ? "" : "admin"}/>
</Lay>
<Lay row=1 col=0 margin=2 weight=0.0 align=$east>
<Label title="Password"/>
</Lay>
<Lay row=1 col=1 margin=2 fill=$horizontal>
<Field.password password event=fieldHandler
value={sys.stage == $prod ? "" : "admin"}/>
</Lay>
<Lay row=2 col=0 margin=2 colSpan=2 fill=$horizontal>
<ProgressBar progress/>
</Lay>
</Panel>
<Panel lay=$south>
<Button loginButn title="Login" image={sys.use("lib/gifs/Apply.gif")}
action={onLogin()} enable={enableLogin()}/>
<Button cancelButn title="Cancel" image={sys.use("lib/gifs/Cancel.gif")}
action={Quest.singleton.exit()}/>
</Panel>
</Panel>
</Frame>
</App>
public Quest () {
super(frame);
progress.title = sys.host == null ? "Standalone" : sys.host;
frame.enter = loginButn;
if (!sys.flash)
checkJag();
Quest Sample Application
291
}
protected void frameHandler (native comp, symbol event) {
if (event == $close)
exit();
}
private void fieldHandler (native comp, symbol event) {
gui.maintain(frame);
}
private boolean enableLogin () {
return loggedIn == $no && username.value != "" && password.value != "";
}
private void onLogin () {
login.run = true;
}
protected vague loginWorker (symbol cmd, vague val) {
switch (cmd) {
case $do:
setLoggedIn($pending);
progress.indefinite = true;
try {
User user = User.login(username.value@string, password.value@string,
!sys.async ? null : new Callback() {
public void completed (vague user) {
setCurrUser(user@User);
onLoginOK();
}
public void failed (Exception e) {
onLoginFail(e);
}
});
if (!sys.async) {
setCurrUser(user);
setLoggedIn($yes);
return getCurrUser();
}
}
catch (Exception e) {
onLoginFail(e);
}
break;
case $done:
if (!sys.async && loggedIn == $yes)
onLoginOK();
break;
}
return null;
}
public void onLoginOK () {
setLoggedIn($yes);
// Replace the login panel with the application panel:
frame.visible = false;
frame -= panel;
sys.call(Util.getSingleton("quest/gui/AppPanel"), $addToFrame, frame);
}
public void onLoginFail (Exception e) {
setLoggedIn($no);
292
JavaGram Agile Development
progress.indefinite = false;
gui.alert("Login Failed", e.getMessage(), frame, $warning);
}
protected void setLoggedIn (symbol status) {
loggedIn = status;
gui.maintain(loginButn);
}
protected void checkJag () {
Upgrade upgrade = new Upgrade() {
protected string getRebootCmd (string jarPath) {
return $"javaw.exe -cp \"{jarPath}\" jag.gui.Gui -host \"{sys.host}\" stage {sys.stage} -root \"{sys.root}\" -ssl \"{sys.ssl}\" -boot quest/Quest";
}
};
// Client-side must have at least JAG1.1:
upgrade.check(1, 1, "jar/JAG.jar", "jar/JAG.jar", sys.loader);
}
public User getCurrUser () {
return currUser;
}
public void setCurrUser (User user) {
currUser = user;
}
public vague getUserId () {
return currUser.getId();
}
public void setCursor (symbol cursor) {
frame.cursor = cursor;
}
public void setStdErr (native textArea) {
app.stdErr = textArea;
}
public static void main () {
Quest.singleton.run();
}
}
</jag>
8.3.2.2 AppPanel
<jag domain="quest/gui">
<load>
"lib/lang/Event"
"lib/gui/NavTree"
"lib/gui/InfoBar"
"lib/gui/Screen"
"quest/bom/User"
"quest/Quest"
"quest/gui/Commands"
"quest/gui/AppTree"
</load>
singleton class AppPanel extends EventListener {
protected Quest app = Quest.singleton;
protected AppTree tree = AppTree.singleton;
protected Commands commands = Commands.singleton;
protected Screen contentScreen;
protected boolean treeVisible = true;
Quest Sample Application
293
protected int lastSplitDivider;
<Panel noContent>
<Layout.border/>
<Label title="No Content" align=$center fgColor="0xFF0000" lay=$center/>
</Panel>
<Null noTree/>
<Panel mainPanel>
<Layout.border/>
<Panel lay=$north>
<Layout.flow align=$west/>
<Indirect ref={commands.getToolBar()}/>
</Panel>
<Pane.split mainSplit lay=$center divider=200 weight=0.3>
<Pane.scroll treeScroll lay=$west>
<Indirect ref={tree.getTree()}/>
</Pane.scroll>
<Indirect ref={noContent} lay=$east/>
</Pane.split>
<Panel lay=$south>
<Layout.border/>
<Indirect ref={InfoBar.singleton.activate()}/>
</Panel>
</Panel>
public AppPanel () {
User.initAllUsersInfo();
tree.addListener(ShowContentEvent, this);
tree.addListener(ShowNoContentEvent, this);
}
public void addToFrame (native frame) {
string title = $"Quest ({app.getCurrUser().getUsername()}) -- ";
title += sys.host == null ? "Standalone" : $"Server: {sys.host}";
frame@<Frame>.title = title;
frame@<Frame>.width = 800;
frame@<Frame>.height = 500;
frame@<Frame>.sizeable = true;
frame += commands.getMenuBar();
frame += mainPanel;
app.setStdErr(InfoBar.singleton.getErrLog());
if (sys.flash)
frame@<Frame>.state = $max; // Maximize the frame so that it fills the browser window
frame@<Frame>.visible = true;
trackTasks(true);
tree.reload();
tree.selectNode($helpRootNode);
gui.maintain(frame);
frame@<Frame>.refresh = true;
}
public void toggleTree () {
if (treeVisible) {
lastSplitDivider = getMainSplitDivider();
mainSplit.west = noTree;
} else {
mainSplit.west = treeScroll;
setMainSplitDivider(lastSplitDivider);
294
JavaGram Agile Development
}
treeVisible = !treeVisible;
}
public boolean consumeEvent (Event event) {
if (event instanceof ShowContentEvent) {
contentScreen = (event@ShowContentEvent).screen;
mainSplit.east = contentScreen.getView();
} else if (event instanceof ShowNoContentEvent) {
mainSplit.east = noContent;
contentScreen = null;
}
return false;
}
public int getMainSplitDivider () {
return mainSplit.divider@int;
}
public void setMainSplitDivider (int div) {
mainSplit.divider = div;
}
public Screen getContentScreen () {
return contentScreen;
}
public void onDownloadTask (vague file) {
app.beginLengthy();
InfoBar.singleton.startTask("Downloading: " + file@string);
}
public void onUploadTask (vague file) {
app.beginLengthy();
InfoBar.singleton.startTask("Uploading: " + file@string);
}
public void onLoadTask (vague script) {
app.beginLengthy();
InfoBar.singleton.startTask("Loading: " + script@string);
}
public void onRemoteTask (vague method) {
app.beginLengthy();
InfoBar.singleton.startTask("Remote call: " + method@string);
}
public void onReadyTask (vague ignore) {
app.endLengthy();
InfoBar.singleton.endTask();
}
public void trackTasks (boolean track) {
sys.hook($download, track ? AppPanel : null, $onDownloadTask);
sys.hook($upload, track ? AppPanel : null, $onUploadTask);
sys.hook($load, track ? AppPanel : null, $onLoadTask);
sys.hook($remote, track ? AppPanel : null, $onRemoteTask);
sys.hook($ready, track ? AppPanel : null, $onReadyTask);
}
}
</jag>
8.3.2.3 AppTree
<jag domain="quest/gui">
<load>
"lib/lang/Common"
Quest Sample Application
295
"lib/bom/Object"
"lib/bom/UserLockableObject"
"lib/bom/Document"
"lib/bom/PickList"
"lib/gui/NavTree"
"lib/gui/Screen"
"lib/gui/Html"
"quest/Quest"
"quest/bom/User"
"quest/bom/Issue"
"quest/gui/AppPanel"
</load>
singleton class AppTree extends NavTree {
int lastSplitDiv;
// Last divider position of main splitter
Document helpRoot = Document.getByPath("quest/doc/help/Root.html");
Document helpContents = Document.getByPath("quest/doc/help/Contents.html");
Document helpReports = Document.getByPath("quest/doc/help/Reports.html");
Document helpSettings = Document.getByPath("quest/doc/help/Settings.html");
<Node userAdminNode image={sys.use("lib/gifs/FindUser.gif")} title="User Admin"
presentation="quest/gui/user/UserSearch"/>
<Node issueSearchNode image={sys.use("lib/gifs/FindFolder.gif")} title="Issue Search"
presentation="quest/gui/issue/IssueSearch"/>
<Node reportsNode image={sys.use("lib/gifs/ReportFolder.gif")} title="Reports"
presentation="quest/gui/misc/HtmlPanel" content={helpReports}/>
<Node settingsNode image={sys.use("lib/gifs/OptionsFolder.gif")} title="Settings"
presentation="quest/gui/misc/HtmlPanel" content={helpSettings}>
<Node logNode image={sys.use("lib/gifs/Report.gif")} title="Log"
presentation="quest/gui/setting/LogPanel"/>
<Node pickListNode image={sys.use("lib/gifs/PickList.gif")} title="Pick Lists"
presentation="quest/gui/setting/PickListPanel"/>
</Node>
<Node helpRootNode image={sys.use("lib/gifs/Help.gif")} title="Help"
presentation="quest/gui/misc/HtmlPanel" content={helpRoot}>
<Node helpContentsNode image={sys.use("lib/gifs/Help.gif")} title="Contents"
presentation="quest/gui/misc/HtmlPanel" content={helpContents}/>
</Node>
public AppTree () {
super(Quest.singleton);
tree += userAdminNode;
tree += issueSearchNode;
tree += reportsNode;
tree += settingsNode;
tree += helpRootNode;
pickListNode.content = new PickLists(!sys.async ? null : new Callback() {
public void completed (vague data) {
pickListNode.content = data;
}
});
}
public boolean selectLogNodeCmd (boolean maintain) {
if (!maintain)
selectNode(logNode);
return true;
}
public void showUser (Object user) {
296
JavaGram Agile Development
showObject(userAdminNode, user, "lib/gifs/User.gif", "quest/gui/user/UserScreen");
}
public void hideUser (vague user) {
hideChildNode(userAdminNode, user, false);
}
public void showIssue (Object issue) {
showObject(issueSearchNode, issue, "lib/gifs/Issue.gif", "quest/gui/issue/IssueScreen");
}
public void hideIssue (vague issue) {
hideChildNode(issueSearchNode, issue, false);
}
public void showHelpPage (Object html) {
showObject(helpRootNode, html, "lib/gifs/Help.gif", "quest/gui/misc/HtmlPanel");
}
protected boolean removableNode (native node) {
vague content = getContent(node);
return content instanceof User || content instanceof Issue;
}
protected boolean canUnlockNode (native node) {
return getContent(node) instanceof UserLockableObject
&& Quest.singleton.getCurrUser().getCanUnlock();
}
protected boolean canDeleteNode (native node) {
return removableNode(node) && Quest.singleton.getCurrUser().getCanDelete();
}
protected void onShowContent (symbol stage) {
if (stage == $before)
lastSplitDiv = AppPanel.singleton.getMainSplitDivider();
else
AppPanel.singleton.setMainSplitDivider(lastSplitDiv);
}
protected void showObject (native parentNode, Object obj, string gifPath, string presentation) {
vague node = findChildNode(parentNode, obj);
if (node == null) {
node = gui.create($Node, map(
$image => sys.use(gifPath),
$title => obj.title(),
$presentation => presentation,
$content => obj
));
parentNode += node;
parentNode@<Node>.reload = true;
}
selectNode(node);
}
public string getSelectedNodeTitle () {
native node = tree.select;
return node == null ? "Untitled" : node@<Node>.title;
}
public boolean printPreviewCmd (boolean maintain) {
Object content = getContent(lastSelNode);
if (content == null || maintain)
return content instanceof Document;
[email protected]();
return true;
}
Quest Sample Application
297
public boolean generateReportCmd (boolean maintain) {
Screen screen = getScreen(lastSelNode);
if (screen == null || maintain)
return !sys.flash && screen instanceof HtmlGenerator;
string path = sys.pathConc(Quest.REPORTS_DIR, AppTree.singleton.getSelectedNodeTitle()
+ ".html");
HtmlGenerator.generateAuto(path, screen, lastSelNode@<Node>.title);
Document report = new Document(path, true);
showObject(reportsNode, report, "lib/gifs/Report.gif", "quest/gui/misc/HtmlPanel");
return true;
}
public boolean showNodeContentCmd (boolean maintain, symbol syntax) {
boolean res = super.showNodeContentCmd(maintain, syntax);
if (!maintain)
selectLogNodeCmd(false);
return res;
}
}
</jag>
8.3.2.4 Commands
<jag domain="quest/gui">
<load>
"lib/gui/GuiUtil"
"lib/gui/PickListMgr"
"quest/Quest"
"quest/gui/AppTree"
"quest/gui/AppPanel"
"quest/gui/user/PasswordDialog"
"quest/gui/misc/AboutDialog"
</load>
singleton class Commands {
AppTree tree = AppTree.singleton;
<MenuBar menuBar>
<Menu title="File">
<MenuItem title="Save" image={sys.use("lib/gifs/Save.gif")}
enable={tree.persistNodeCmd(true)} action={tree.persistNodeCmd(false)}
accelerator="control S"/>
<MenuItem title="Save All" image={sys.use("lib/gifs/SaveAll.gif")}
enable={tree.persistAllNodesCmd(true)} action={tree.persistAllNodesCmd(false)}/>
<Separator/>
<MenuItem title="Print Preview" image={sys.use("lib/gifs/DrillDown.gif")}
enable={filePrintPreviewCmd(true)} action={filePrintPreviewCmd(false)}
accelerator="control P"/>
<Separator/>
<MenuItem title="Exit" image = {GuiUtil.blankIcon} action={fileExitCmd()}/>
</Menu>
<Menu title="Edit">
<MenuItem title="Hide" image={sys.use("lib/gifs/HideNode.gif")}
enable={tree.hideNodeCmd(true)} action={tree.hideNodeCmd(false)}/>
<MenuItem title="Unlock" image={sys.use("lib/gifs/Unlock.gif")}
enable={tree.unlockNodeCmd(true)} action={tree.unlockNodeCmd(false)}/>
<Separator/>
<MenuItem title="Delete" image={sys.use("lib/gifs/DeleteNode.gif")}
298
JavaGram Agile Development
enable={tree.deleteNodeCmd(true)} action={tree.deleteNodeCmd(false)}/>
</Menu>
<Menu title="View">
<MenuItem title="Previous Tree Node" image={sys.use("lib/gifs/Back.gif")}
enable={tree.goBackCmd(true)} action={tree.goBackCmd(false)}/>
<MenuItem title="Next Tree Node" image={sys.use("lib/gifs/Forward.gif")}
enable={tree.goForwardCmd(true)} action={tree.goForwardCmd(false)}/>
<MenuItem title="Reload Tree" image={sys.use("lib/gifs/Refresh.gif")}
action={tree.reload()}/>
<Separator/>
<MenuItem title="Show Log" image={sys.use("lib/gifs/Report.gif")}
enable={tree.selectLogNodeCmd(true)} action={tree.selectLogNodeCmd(false)}/>
</Menu>
<Menu title="Tools">
<MenuItem title="Change Password..." image={sys.use("lib/gifs/Rename.gif")}
action={toolsChangePasswordCmd()}/>
<Separator/>
<MenuItem title="Generate Report..." image={sys.use("lib/gifs/GenReport.gif")}
enable={toolsGenReportCmd(true)} action={toolsGenReportCmd(false)}/>
<Separator/>
<MenuItem title="Refresh Pick Lists" image={sys.use("lib/gifs/RefreshCombo.gif")}
enable={toolsRefreshPickListsCmd(true)} action={toolsRefreshPickListsCmd(false)}/>
</Menu>
<Menu title="Help">
<MenuItem title="User Guide..." image={sys.use("lib/gifs/Help.gif")}
action={helpUserGuideCmd()}/>
<Separator/>
<MenuItem title="About Quest..." image={sys.use("lib/gifs/Question.gif")}
action={helpAboutCmd()}/>
</Menu>
</MenuBar>
<ToolBar toolBar floatable=true border=$empty>
<Button tooltip="Save Node" image={sys.use("lib/gifs/Save.gif")}
enable={tree.persistNodeCmd(true)} action={tree.persistNodeCmd(false)}/>
<Button tooltip="Save All Nodes" image={sys.use("lib/gifs/SaveAll.gif")}
enable={tree.persistAllNodesCmd(true)} action={tree.persistAllNodesCmd(false)}/>
<Separator/>
<Button tooltip="Print Preview" image={sys.use("lib/gifs/DrillDown.gif")}
enable={filePrintPreviewCmd(true)} action={filePrintPreviewCmd(false)}/>
<Separator/>
<Button tooltip="Hide Tree Node" image={sys.use("lib/gifs/HideNode.gif")}
enable={tree.hideNodeCmd(true)} action={tree.hideNodeCmd(false)}/>
<Button tooltip="Unlock Tree Node" image={sys.use("lib/gifs/Unlock.gif")}
enable={tree.unlockNodeCmd(true)} action={tree.unlockNodeCmd(false)}/>
<Button tooltip="Delete Tree Node" image={sys.use("lib/gifs/DeleteNode.gif")}
enable={tree.deleteNodeCmd(true)} action={tree.deleteNodeCmd(false)}/>
<Separator/>
<Button.toggle tooltip="Toggle Navigation Tree" image={sys.use("lib/gifs/ShowTree.gif")}
select=true action={viewShowHideTreeCmd()}/>
<Separator/>
<Button tooltip="Previous Tree Node" image={sys.use("lib/gifs/Back.gif")}
enable={tree.goBackCmd(true)} action={tree.goBackCmd(false)}/>
<Button tooltip="Next Tree Node" image={sys.use("lib/gifs/Forward.gif")}
enable={tree.goForwardCmd(true)} action={tree.goForwardCmd(false)}/>
<Button tooltip="Reload Tree Node" image={sys.use("lib/gifs/Refresh.gif")}
Quest Sample Application
299
action={tree.reload()}/>
<Separator/>
<Button tooltip="Generate Report" image={sys.use("lib/gifs/GenReport.gif")}
enable={toolsGenReportCmd(true)} action={toolsGenReportCmd(false)}/>
<Separator/>
<Button tooltip="Show Node XML Data" image={sys.use("lib/gifs/ViewXml.gif")}
enable={toolsShowXmlCmd(true)} action={toolsShowXmlCmd(false)}/>
<Button tooltip="Show Node JAG Data" image={sys.use("lib/gifs/ViewJAG.gif")}
enable={toolsShowJagCmd(true)} action={toolsShowJagCmd(false)}/>
</ToolBar>
public native getMenuBar () {
return menuBar;
}
public native getToolBar () {
return toolBar;
}
protected boolean filePrintPreviewCmd (boolean maintain) {
return AppTree.singleton.printPreviewCmd(maintain);
}
protected void fileExitCmd () {
if (tree.canExit())
Quest.singleton.exit();
}
protected void viewShowHideTreeCmd () {
AppPanel.singleton.toggleTree();
}
protected void toolsChangePasswordCmd () {
PasswordDialog.singleton.show(Quest.singleton.getCurrUser());
}
protected boolean toolsGenReportCmd (boolean maintain) {
return AppTree.singleton.generateReportCmd(maintain);
}
protected boolean toolsShowXmlCmd (boolean maintain) {
return AppTree.singleton.showNodeContentCmd(maintain, $xml);
}
protected boolean toolsShowJagCmd (boolean maintain) {
return AppTree.singleton.showNodeContentCmd(maintain, $jag);
}
protected boolean toolsRefreshPickListsCmd (boolean maintain) {
if (!maintain)
PickListMgr.singleton.refreshLists();
return true;
}
protected void helpUserGuideCmd () {
AppTree.singleton.selectNode($helpRootNode);
}
protected void helpAboutCmd () {
AboutDialog.singleton.show();
}
}
</jag>
300
JavaGram Agile Development
8.3.3 User Admin
8.3.3.1 UserSearch
<jag domain="quest/gui/user">
<load>
"lib/lang/Common"
"lib/bom/Object"
"lib/gui/Table"
"lib/gui/Search"
"lib/gui/Html"
"quest/Quest"
"quest/bom/User"
"quest/gui/AppTree"
</load>
singleton class UserSearch extends SearchScreen, HtmlGenerator {
static final vector<map> TABLE_FORMAT = [
[$key=>$id, $title=>"ID", $width=>10, $align=>$east]
,[$key=>$username, $title=>"Username", $width=>100, $align=>$west]
,[$key=>$lastName, $title=>"Last Name", $width=>100, $align=>$west]
,[$key=>$firstName, $title=>"First Name", $width=>100, $align=>$west]
,[$key=>$active, $title=>"Active", $width=>20, $align=>$center]
,[$key=>$canUnlock, $title=>"Can Unlock", $width=>30, $align=>$center]
,[$key=>$canDelete, $title=>"Can Delete", $width=>30, $align=>$center]
];
static final vector<symbol> PIE_KEYS = [$active, $canUnlock, $canDelete];
<Panel criteria>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 weight=0.0 align=$east>
<Label title="Search"/>
</Lay>
<Lay row=0 col=1 margin=2 fill=$horizontal>
<Hot:Combo byCombo key=$searchBy data=["By Criteria", "By ID"]/>
</Lay>
<Lay row=0 col=2 margin=2 weight=0.0 align=$east>
<Label title="ID"/>
</Lay>
<Lay row=0 col=3 margin=2 fill=$horizontal>
<Field.text key=$id enable={isIdSearch()}/>
</Lay>
<Lay row=2 col=0 margin=2 weight=0.0 align=$east>
<Label title="Username"/>
</Lay>
<Lay row=2 col=1 margin=2 fill=$horizontal>
<Hot:Field.text key=$username enable={isCriteriaSearch()}/>
</Lay>
<Lay row=2 col=2 margin=2 weight=0.0 align=$east>
<Label title="Last Name"/>
</Lay>
<Lay row=2 col=3 margin=2 fill=$horizontal>
<Hot:Field.text key=$lastName enable={isCriteriaSearch()}/>
</Lay>
<Lay row=2 col=4 margin=2 weight=0.0 align=$east>
<Label title="First Name"/>
</Lay>
<Lay row=2 col=5 margin=2 fill=$horizontal>
Quest Sample Application
301
<Hot:Field.text key=$firstName enable={isCriteriaSearch()}/>
</Lay>
<Lay row=3 col=0 margin=2 weight=0.0 align=$east>
<Label title="Active"/>
</Lay>
<Lay row=3 col=1 margin=2 fill=$horizontal>
<Hot:Combo key=$active enable={isCriteriaSearch()} data={TICK_DATA}/>
</Lay>
<Lay row=3 col=2 margin=2 weight=0.0 align=$east>
<Label title="Can Lock"/>
</Lay>
<Lay row=3 col=3 margin=2 fill=$horizontal>
<Hot:Combo key=$canUnlock enable={isCriteriaSearch()} data={TICK_DATA}/>
</Lay>
<Lay row=3 col=4 margin=2 weight=0.0 align=$east>
<Label title="Can Delete"/>
</Lay>
<Lay row=3 col=5 margin=2 fill=$horizontal>
<Hot:Combo key=$canDelete enable={isCriteriaSearch()} data={TICK_DATA}/>
</Lay>
</Panel>
public UserSearch () {
super@SearchScreen(
new SearchCriteriaPanel(this, criteria),
new TableAndPieSearchResultScreen(this, TABLE_FORMAT,
new TableButtons(TableButtons.ALL_BUTNS), PIE_KEYS) {
protected Object drillRow (Object bo) {
AppTree.singleton.showUser(bo);
return bo;
}
protected vague createRow (Callback cb = null) {
User user = new User([@UserDetails username=>"NewUser",
password=>"password", active=>true]);
cb?.completed(user);
return user;
}
protected vague deleteRow (int idx, Callback cb = null) {
vague row = [email protected](idx, !sys.async ? null : new Callback(cb) {
public void completed (vague data) {
AppTree.singleton.hideUser(rowToObject(data));
}
});
if (!sys.async)
AppTree.singleton.hideUser(rowToObject(row));
return row;
}
}
);
getResultScreen().setEditable(Quest.singleton.getCurrUser().isAdminUser());
}
protected boolean isIdSearch () {
return byCombo.value == "By ID";
}
protected boolean isCriteriaSearch () {
return byCombo.value == "By Criteria";
}
302
JavaGram Agile Development
protected vector find (map criteria, Callback cb = null) {
if (criteria.$searchBy == "By ID") {
User user = User.findById(criteria.$id, cb);
return user == null ? vector() : vector(user);
} else {
return User.find(criteria, cb);
}
}
public void clearCmd () {
[email protected]();
AppTree.singleton.hideUser($all);
}
}
</jag>
8.3.3.2 UserScreen
<jag domain="quest/gui/user">
<load>
"lib/gui/Screen"
"lib/gui/Html"
"quest/bom/User"
"quest/gui/user/PasswordDialog"
"quest/Quest"
</load>
singleton class UserScreen extends Screen, HtmlGenerator {
<Panel view enable={Quest.singleton.getCurrUser().isAdminUser()}>
<Layout.border/>
<Panel lay=$north title="User Details" enable=true>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 weight=0.0 align=$east>
<Label title="ID"/>
</Lay>
<Lay row=0 col=1 margin=2 fill=$horizontal>
<Field.number key=$id readOnly=true precision=0/>
</Lay>
<Lay row=0 col=5 margin=2 weight=0.0 align=$east>
<Button title="Change Password" image={sys.use("lib/gifs/Hammer.gif")}
enable={canChangePassword()} action={changePassword()}/>
</Lay>
<Lay
<Lay
<Lay
<Lay
<Lay
<Lay
row=1
row=1
row=1
row=1
row=1
row=1
col=0
col=1
col=2
col=3
col=4
col=5
margin=2
margin=2
margin=2
margin=2
margin=2
margin=2
weight=0.0 align=$east><Label title="Username"/></Lay>
fill=$horizontal><Hot:Field.text key=$username/></Lay>
weight=0.0 align=$east><Label title="Last Name"/></Lay>
fill=$horizontal><Hot:Field.text key=$lastName/></Lay>
weight=0.0 align=$east><Label title="First Name"/></Lay>
fill=$horizontal><Hot:Field.text key=$firstName/></Lay>
<Lay row=2 col=1 align=$west><Hot:Option.tick key=$active title="Active"/></Lay>
<Lay row=2 col=3 align=$west><Hot:Option.tick key=$canUnlock title="Can Unlock"/></Lay>
<Lay row=2 col=5 align=$west><Hot:Option.tick key=$canDelete title="Can Delete"/></Lay>
<Lay row=3 col=0 margin=2 weight=0.0 align=$east><Label title="Email"/></Lay>
<Lay row=3 col=1 margin=2 colSpan=5 fill=$horizontal><Hot:Field.text key=$email/></Lay>
</Panel>
<Panel lay=$center title="Note">
<Layout.border/>
Quest Sample Application
303
<Pane.scroll lay=$center>
<Hot:Area.text key=$note/>
</Pane.scroll>
</Panel>
</Panel>
public UserScreen () {
setEditable(Quest.singleton.getCurrUser().isAdminUser());
}
protected boolean canChangePassword () {
return Quest.singleton.getCurrUser().isAdminUser();
}
protected void changePassword () {
PasswordDialog.singleton.show(getContent()@User);
}
}
</jag>
8.3.3.3 PasswordDialog
<jag domain="quest/gui/user">
<load>
"lib/lang/Common"
"lib/gui/GuiUtil"
"quest/Quest"
"quest/bom/User"
</load>
singleton class PasswordDialog {
User user;
<Dialog dialog parent={GuiUtil.getMainFrame()} center={GuiUtil.getMainFrame()} fixed=true
modal=true title="Change Password" width=250 height=140>
<Layout.border/>
<Panel lay=$center>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 weight=0.0 align=$east>
<Label title="Old Password"/>
</Lay>
<Lay row=0 col=1 margin=2 fill=$horizontal>
<Field.password oldPasswd enable={oldPassRequired()} event=fieldHandler/>
</Lay>
<Lay row=1 col=0 margin=2 weight=0.0 align=$east>
<Label title="New Password"/>
</Lay>
<Lay row=1 col=1 margin=2 fill=$horizontal>
<Field.password newPasswd event=fieldHandler/>
</Lay>
<Lay row=2 col=0 margin=2 weight=0.0 align=$east>
<Label title="Confirm Password"/>
</Lay>
<Lay row=2 col=1 margin=2 fill=$horizontal>
<Field.password confirmPasswd event=fieldHandler/>
</Lay>
</Panel>
<Panel lay=$south>
<Button changeButn title="Change" image={sys.use("lib/gifs/Apply.gif")}
304
JavaGram Agile Development
action={onChange()} enable={enableChange()}/>
<Button cancelButn title="Cancel" image={sys.use("lib/gifs/Cancel.gif")}
action={onCancel()}/>
</Panel>
</Dialog>
public void show (User user) {
this.user = user;
oldPasswd.value = "";
newPasswd.value = "";
confirmPasswd.value = "";
gui.maintain(dialog);
dialog.enter = changeButn;
dialog.show = true;
}
protected void fieldHandler (native comp, symbol event) {
gui.maintain(dialog);
}
protected void onChange () {
string oldPass @= oldPasswd.value;
string newPass @= newPasswd.value;
string confirmPass @= confirmPasswd.value;
if (newPass != confirmPass) {
gui.alert("Failed", "New Password doesn't match Cofirm Password!", dialog, $warning);
return;
}
if (oldPassRequired()) {
if (!user.checkPassword(oldPass)) {
gui.alert("Failed", "Old Password is invalid!", dialog, $warning);
return;
}
if (oldPass == newPass) {
gui.alert("Failed", "New Password is the same as Old Password!", dialog, $warning);
return;
}
}
try {
user.changePassword(newPass, !sys.async ? null : new Callback() {
public void completed (vague data) {
dialog.show = false;
}
public void failed (Exception e) {
passwordFailed(e);
}
});
}
catch (Exception e) {
passwordFailed(e);
return;
}
if (!sys.async)
dialog.show = false;
}
protected void passwordFailed (Exception e) {
gui.alert("Failed", e.getMessage(), dialog, $warning);
}
Quest Sample Application
305
protected void onCancel () {
dialog.show = false;
}
protected boolean oldPassRequired () {
return !Quest.singleton.getCurrUser().isAdminUser() || user.isAdminUser();
}
protected boolean enableChange () {
return (!oldPassRequired() || oldPasswd.value != "")
&& newPasswd.value != "" && confirmPasswd.value != "";
}
}
</jag>
8.3.4 Issue Management
8.3.4.1 IssueSearch
<jag domain="quest/gui/issue">
<load>
"lib/lang/Common"
"lib/bom/Object"
"lib/gui/GuiUtil"
"lib/gui/Table"
"lib/gui/Search"
"lib/gui/Html"
"lib/gui/PickListMgr"
"quest/bom/User"
"quest/bom/Issue"
"quest/bom/Rules"
"quest/gui/AppTree"
"quest/Quest"
</load>
singleton class IssueSearch extends SearchScreen, HtmlGenerator {
static final vector<map> TABLE_FORMAT = [
[$key=>$id, $title=>"ID", $width=>20, $align=>$east]
,[$key=>$title, $title=>"Title", $width=>100, $align=>$west]
,[$key=>$project, $title=>"Project", $width=>50, $align=>$west]
,[$key=>$module, $title=>"Module", $width=>50, $align=>$west]
,[$key=>$build, $title=>"Build", $width=>50, $align=>$west]
,[$key=>$type, $title=>"Type", $width=>50, $align=>$west]
,[$key=>$state, $title=>"State", $width=>50, $align=>$west]
,[$key=>$severity, $title=>"Severity", $width=>50, $align=>$west]
,[$key=>$priority, $title=>"Priority", $width=>50, $align=>$west]
,[$key=>$raisedByName, $title=>"Raised By", $width=>50, $align=>$west]
,[$key=>$assignedToName, $title=>"Assigned To", $width=>50, $align=>$west]
];
static final vector<symbol> PIE_KEYS = [$project, $type, $state, $severity, $priority];
int reportNo = 0;
<Panel criteria>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 weight=0.0 align=$east>
<Label title="Search"/>
</Lay>
<Lay row=0 col=1 margin=2 fill=$horizontal>
<Hot:Combo byCombo key=$searchBy data=["By Criteria", "By ID"]/>
306
JavaGram Agile Development
</Lay>
<Lay row=0 col=2 margin=2 weight=0.0 align=$east>
<Label title="ID"/>
</Lay>
<Lay row=0 col=3 margin=2 fill=$horizontal>
<Field.text key=$id enable={isIdSearch()}/>
</Lay>
<Lay row=1 col=0 margin=2 weight=0.0 align=$east>
<Label title="Title"/>
</Lay>
<Lay row=1 col=1 margin=2 colSpan=3 fill=$horizontal>
<Field.text key=$title enable={isCriteriaSearch()}/>
</Lay>
<Lay row=2 col=0 margin=2 weight=0.0 align=$east>
<Button title="Project" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Project)}/>
</Lay>
<Lay row=2 col=1 margin=2 fill=$horizontal>
<Hot:Combo projCombo key=$project enable={isCriteriaSearch()}
data={PickListMgr.singleton.registerList($Project, projCombo)}/>
</Lay>
<Lay row=2 col=2 margin=2 weight=0.0 align=$east>
<Label title="Module"/>
</Lay>
<Lay row=2 col=3 margin=2 fill=$horizontal>
<Hot:Field.text key=$module enable={isCriteriaSearch()}/>
</Lay>
<Lay row=2 col=4 margin=2 weight=0.0 align=$east>
<Label title="Build"/>
</Lay>
<Lay row=2 col=5 margin=2 fill=$horizontal>
<Hot:Field.text key=$build enable={isCriteriaSearch()}/>
</Lay>
<Lay row=3 col=0 margin=2 weight=0.0 align=$east>
<Button title="Type" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Type)}/>
</Lay>
<Lay row=3 col=1 margin=2 fill=$horizontal>
<Hot:Combo typeCombo key=$type enable={isCriteriaSearch()}
data={PickListMgr.singleton.registerList($Type, typeCombo)}/>
</Lay>
<Lay row=3 col=2 margin=2 weight=0.0 align=$east>
<Label title="State"/>
</Lay>
<Lay row=3 col=3 margin=2 fill=$horizontal>
<Hot:Combo key=$state enable={isCriteriaSearch()}
data={Rules.singleton.getStates()}/>
</Lay>
<Lay row=3 col=4 margin=2 weight=0.0 align=$east>
<Button title="Severity" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Severity)}/>
</Lay>
<Lay row=3 col=5 margin=2 fill=$horizontal>
Quest Sample Application
307
<Hot:Combo sevCombo key=$severity enable={isCriteriaSearch()}
data={PickListMgr.singleton.registerList($Severity, sevCombo)}/>
</Lay>
<Lay row=4 col=0 margin=2 weight=0.0 align=$east>
<Button title="Priority" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Priority)}/>
</Lay>
<Lay row=4 col=1 margin=2 fill=$horizontal>
<Hot:Combo priorCombo key=$priority enable={isCriteriaSearch()}
data={PickListMgr.singleton.registerList($Priority, priorCombo)}/>
</Lay>
<Lay row=4 col=2 margin=2 weight=0.0 align=$east>
<Label title="Raised By"/>
</Lay>
<Lay row=4 col=3 margin=2 fill=$horizontal>
<Hot:Combo key=$raisedBy enable={isCriteriaSearch()} model=userModel/>
</Lay>
<Lay row=4 col=4 margin=2 weight=0.0 align=$east>
<Label title="Assigned To"/>
</Lay>
<Lay row=4 col=5 margin=2 fill=$horizontal>
<Hot:Combo key=$assignedTo enable={isCriteriaSearch()} model=userModel/>
</Lay>
</Panel>
public IssueSearch () {
super@SearchScreen(
new SearchCriteriaPanel(this, criteria),
new TableAndPieSearchResultScreen(this, TABLE_FORMAT,
new TableButtons([$details, $new, $delete, $jump]), PIE_KEYS) {
protected Object drillRow (Object bo) {
Callback cb = null;
if (sys.async) {
cb = new Callback() {
public void completed (vague data) {
Issue issue @= bo;
if (issue.getAttach() != null && issue.getHistory() != null)
AppTree.singleton.showIssue(bo);
}
public void failed (Exception e) {
gui.alert("Can get issue details", e.getMessage(),
GuiUtil.getMainFrame(), $error);
}
};
}
[email protected](cb);
[email protected](cb);
if (!sys.async)
AppTree.singleton.showIssue(bo);
return bo;
}
protected vague createRow (Callback cb = null) {
int userId @= Quest.singleton.getUserId();
Issue issue = new Issue(object(IssueDetails, title=>"NewIssue", state=>"New"
, raisedBy=>userId, raisedOn=>sys.date()));
issue.persist() ?? cb != null -> cb.completed(issue);
308
JavaGram Agile Development
return issue;
}
protected vague deleteRow (int idx, Callback cb = null) {
vague row = [email protected](idx, !sys.async ? null : new Callback(cb) {
public void completed (vague data) {
AppTree.singleton.hideIssue(rowToObject(data));
}
});
AppTree.singleton.hideIssue(rowToObject(row));
return row;
}
}
);
getResultScreen().setCanDeleteRow(Quest.singleton.getCurrUser().getCanDelete());
}
protected boolean isIdSearch () {
return byCombo.value == "By ID";
}
protected boolean isCriteriaSearch () {
return byCombo.value == "By Criteria";
}
protected vector find (map criteria, Callback cb = null) {
if (criteria.$searchBy == "By ID") {
Issue issue = Issue.findById(criteria.$id, cb);
return issue == null ? vector() : vector(issue);
} else {
return Issue.find(criteria, cb);
}
}
public void clearCmd () {
[email protected]();
AppTree.singleton.hideIssue($all);
}
protected vague userModel (native combo, symbol cmd, int idx) {
return User.userModel(false, cmd, idx);
}
}
</jag>
8.3.4.2 IssueScreen
<jag domain="quest/gui/issue">
<load>
"lib/lang/Common"
"lib/gui/Screen"
"lib/gui/GuiUtil"
"lib/gui/Html"
"lib/bom/Object"
"quest/bom/Issue"
"quest/bom/Attachment"
"quest/bom/History"
"quest/bom/User"
"quest/bom/Rules"
"quest/Quest"
"quest/gui/AppTree"
"lib/gui/PickListMgr"
"quest/gui/issue/AssignDialog"
Quest Sample Application
309
"quest/gui/issue/ResolveDialog"
"quest/gui/issue/RejectDialog"
</load>
singleton class IssueScreen extends Screen, HtmlGenerator {
static final vector<map> ATTACH_TABLE_FORMAT = [
[$key=>$name, $title=>"File Name", $width=>200, $align=>$west]
,[$key=>$path, $title=>"Local Name or Path", $width=>400, $align=>$west]
,[$key=>$modified, $title=>"Modified", $width=>150, $align=>$east]
,[$key=>$size, $title=>"Size", $width=>50, $align=>$east]
];
static final vector<map> HIST_TABLE_FORMAT = [
[$key=>$actorName, $title=>"Actioned By", $width=>100, $align=>$west]
,[$key=>$action, $title=>"Action", $width=>50, $align=>$west, $format=>$name]
,[$key=>$actDate, $title=>"Date", $width=>50, $align=>$west]
,[$key=>$oldState, $title=>"Old State", $width=>50, $align=>$west]
,[$key=>$newState, $title=>"New State", $width=>50, $align=>$west]
,[$key=>$note, $title=>"Note", $width=>200, $align=>$west]
];
string lastAttachDir = "C:/";
<Panel view>
<Layout.border/>
<Panel lay=$north title="Issue Details" enable=true>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 weight=0.0 align=$east>
<Label title="ID"/>
</Lay>
<Lay row=0 col=1 margin=2 fill=$horizontal>
<Field.number key=$id readOnly=true precision=0/>
</Lay>
<Lay row=1 col=0 margin=2 weight=0.0 align=$east>
<Label title="Title"/>
</Lay>
<Lay row=1 col=1 margin=2 colSpan=3 fill=$horizontal>
<Hot:Field.text key=$title/>
</Lay>
<Lay row=1 col=4 margin=2 weight=0.0 align=$east>
<Label title="Raised On"/>
</Lay>
<Lay row=1 col=5 margin=2 fill=$horizontal>
<Field.date key=$raisedOn format={GuiUtil.DATE_TIME_FORMAT} readOnly=true/>
</Lay>
<Lay row=2 col=0 margin=2 weight=0.0 align=$east>
<Button title="Project" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Project)}/>
</Lay>
<Lay row=2 col=1 margin=2 fill=$horizontal>
<Hot:Combo projCombo key=$project
data={PickListMgr.singleton.registerList($Project, projCombo)}/>
</Lay>
<Lay row=2 col=2 margin=2 weight=0.0 align=$east>
<Label title="Module"/>
</Lay>
<Lay row=2 col=3 margin=2 fill=$horizontal>
310
JavaGram Agile Development
<Hot:Field.text key=$module/>
</Lay>
<Lay row=2 col=4 margin=2 weight=0.0 align=$east>
<Label title="Build"/>
</Lay>
<Lay row=2 col=5 margin=2 fill=$horizontal>
<Hot:Field.text key=$build/>
</Lay>
<Lay row=3 col=0 margin=2 weight=0.0 align=$east>
<Button title="Type" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Type)}/>
</Lay>
<Lay row=3 col=1 margin=2 fill=$horizontal>
<Hot:Combo typeCombo key=$type
data={PickListMgr.singleton.registerList($Type, typeCombo)}/>
</Lay>
<Lay row=3 col=2 margin=2 weight=0.0 align=$east>
<Label title="State"/>
</Lay>
<Lay row=3 col=3 margin=2 fill=$horizontal>
<Field.text stateField key=$state readOnly=true/>
</Lay>
<Lay row=3 col=4 margin=2 weight=0.0 align=$east>
<Button title="Severity" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Severity)}/>
</Lay>
<Lay row=3 col=5 margin=2 fill=$horizontal>
<Hot:Combo sevCombo key=$severity
data={PickListMgr.singleton.registerList($Severity, sevCombo)}/>
</Lay>
<Lay row=4 col=0 margin=2 weight=0.0 align=$east>
<Button title="Priority" image={Quest.HAMMER}
action={PickListMgr.singleton.editList($Priority)}/>
</Lay>
<Lay row=4 col=1 margin=2 fill=$horizontal>
<Hot:Combo priorCombo key=$priority
data={PickListMgr.singleton.registerList($Priority, priorCombo)}/>
</Lay>
<Lay row=4 col=2 margin=2 weight=0.0 align=$east>
<Label title="Raised By"/>
</Lay>
<Lay row=4 col=3 margin=2 fill=$horizontal>
<Field.text key=$raisedByName readOnly=true/>
</Lay>
<Lay row=4 col=4 margin=2 weight=0.0 align=$east>
<Label title="Assigned To"/>
</Lay>
<Lay row=4 col=5 margin=2 fill=$horizontal>
<Field.text assignedToField key=$assignedToName readOnly=true/>
</Lay>
</Panel>
<Pane.tabbed lay=$center>
<Tab title="Description" image={sys.use("lib/gifs/Comment.gif")}>
<Pane.scroll>
Quest Sample Application
311
<Hot:Area.text key=$description/>
</Pane.scroll>
</Tab>
<Tab title="Resolution" image={sys.use("lib/gifs/Apply.gif")}>
<Pane.scroll>
<Hot:Area.text key=$resolution/>
</Pane.scroll>
</Tab>
<Tab title="Attachments" image={sys.use("lib/gifs/Attachment.gif")}>
<Panel attachPanel>
<Layout.border/>
<Pane.scroll lay=$center>
<Table attachTable key=$attach autoSize=true styled=true
format={ATTACH_TABLE_FORMAT} event=attachHandler/>
</Pane.scroll>
<Panel lay=$south>
<Button title="Attach..." image={sys.use("lib/gifs/Attachment.gif")}
enable={canAttachDoc()} action={attachDoc()}/>
<Button title="View" image={sys.use("lib/gifs/DrillDown.gif")}
enable={canViewDoc()} action={viewDoc(false)}/>
<Button title="Edit" image={sys.use("lib/gifs/Comment.gif")}
enable={canEditDoc()} action={viewDoc(true)}/>
<Button title="Save" image={sys.use("lib/gifs/Save.gif")}
enable={canSaveDoc()} action={saveDoc()}/>
<Button title="Unlock" image={sys.use("lib/gifs/Unlock.gif")}
enable={canUnlockDoc()} action={unlockDoc(true)}/>
<Button title="Delete" image={sys.use("lib/gifs/Delete.gif")}
enable={canDeleteDoc()} action={deleteDoc()}/>
</Panel>
</Panel>
</Tab>
<Tab title="History" image={sys.use("lib/gifs/Calendar.gif")}>
<Pane.scroll>
<Table histTable key=$history autoSize=true styled=true
format={HIST_TABLE_FORMAT}/>
</Pane.scroll>
</Tab>
</Pane.tabbed>
<Panel lay=$east>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 fill=$horizontal>
<Button title="Submit" image={sys.use("lib/gifs/RightArrow.gif")}
enable={actionAllowed($submit)} action={submit()}/>
</Lay>
<Lay row=1 col=0 margin=2 fill=$horizontal>
<Button title="Assign" image={sys.use("lib/gifs/Assign.gif")}
enable={actionAllowed($assign)} action={assign()}/>
</Lay>
<Lay row=2 col=0 margin=2 fill=$horizontal>
<Button title="Open" image={sys.use("lib/gifs/Open.gif")}
enable={actionAllowed($open)} action={open()}/>
</Lay>
<Lay row=3 col=0 margin=2 fill=$horizontal>
<Button title="Resolve" image={sys.use("lib/gifs/Resolve.gif")}
enable={actionAllowed($resolve)} action={resolve()}/>
</Lay>
312
JavaGram Agile Development
<Lay row=4 col=0 margin=2 fill=$horizontal>
<Button title="Reject" image={sys.use("lib/gifs/Reject.gif")}
enable={actionAllowed($reject)} action={reject()}/>
</Lay>
<Lay row=5 col=0 margin=2 fill=$horizontal>
<Button title="Close" image={sys.use("lib/gifs/Close.gif")}
enable={actionAllowed($close)} action={close()}/>
</Lay>
<Lay row=6 col=0 margin=2 fill=$horizontal>
<Button title="Reopen" image={sys.use("lib/gifs/Open.gif")}
enable={actionAllowed($reopen)} action={reopen()} enable={canReopen()}/>
</Lay>
</Panel>
</Panel>
<FileChooser fileChooser owner={GuiUtil.getMainFrame()} multiSelect=false file=true
title="Select the file to attach" path={lastAttachDir}/>
protected Attachment getSelectedAttachment () {
int row @= attachTable.select;
return (row == null ? null : attachTable.data[row]) @ Attachment;
}
protected string getSelectedAttachmentName () {
Attachment doc = getSelectedAttachment();
return doc == null ? "unnamed" : doc.getName();
}
protected void attachHandler (native comp, symbol event) {
switch (event) {
case $select:
gui.maintain(attachPanel);
break;
case $drill:
viewDoc(false);
break;
}
}
protected boolean canAttachDoc () {
return editable;
}
protected void attachDoc () {
int issueId @= getContent()@Issue.getId();
if (sys.flash) {
sys.upload(null, Attachment.genUniqueTempFilePath(), null, true, $attachCB
, list(issueId));
return;
}
string path @= fileChooser.show;
if (path == null)
return;
map<symbol, string> parts = sys.pathParts(path);
map props = sys.pathProps(path);
lastAttachDir @= parts[$dir];
string name = parts[$name] + (parts[$ext] == "" ? "" : "." + parts[$ext]);
AttachmentDetails details = object(AttachmentDetails,
issueId => issueId, name => name,
size => props[$length]@int, modified => props[$modified]@date
);
Quest Sample Application
313
Attachment doc = new Attachment(details, path);
saveAndRefresh(doc, false);
}
protected void attachCB (vague res, int issueId) {
if (res instanceof map) {
map info @= res;
AttachmentDetails details = object(AttachmentDetails,
issueId => issueId, name => info.$local@string,
size => info.$size@int, modified => sys.date(info.$timestamp@int)
);
Attachment doc = new Attachment(details, info.$remote@string);
doc.saveUploaded(info.$remote@string, !sys.async ? null : new Callback() {
public void completed (vague data) {
refreshAttachments();
}
});
}
}
protected boolean canViewDoc () {
return getSelectedAttachment() != null;
}
protected void viewDoc (boolean edit) {
Attachment doc = getSelectedAttachment();
if (sys.flash) {
doc.retrieve(null, new Callback() {
public void completed (vague data) {
attachTable.refresh = true;
}
});
} else {
try {
doc.refresh()
?> sys.async
-> afterRefreshDoc(doc, edit)
-> refreshDocFailed(doc, sys.getAsyncErr());
}
catch (Exception e) {
refreshDocFailed(doc, e);
return;
}
}
}
protected void afterRefreshDoc (Attachment doc, boolean edit) {
int lockedBy @= doc.lockedBy();
boolean lockedByMe = lockedBy == Quest.singleton.getUserId();
boolean lockedByOther = lockedBy != null && !lockedByMe;
string dir = Attachment.recommendLocalDir(doc.getIssueId());
string path = sys.pathConc(dir, doc.getName());
boolean exists = sys.pathExists(path);
if (edit && lockedByOther) {
gui.alert("Can't edit " + doc.getName(), "File is currently locked by "
+ User.getUserInfo(false, lockedBy)[$name]@string, GuiUtil.getMainFrame(), $waring);
refreshAttachments();
return;
}
map pathProps = exists ? sys.pathProps(path) : null;
314
JavaGram Agile Development
boolean useLocalCopy = exists && (lockedByMe || pathProps[$modified] == doc.getModified());
boolean lockIt = edit && !lockedByMe;
symbol amend;
if (!edit && !lockedByMe)
amend = $makeReadOnly;
else if (exists && edit && !pathProps[$writeable]@boolean)
amend = $makeWriteable;
if (useLocalCopy) {
doc.setSize(pathProps[$length]@int);
doc.setPath(path);
} else {
path = doc.retrieve(dir);
}
if (lockIt) {
try {
doc.lock(Quest.singleton.getUserId(), !sys.async ? null : new Callback() {
public void completed (vague data) {
afterLockDoc(data@string, amend);
}
public void failed (Exception e) {
failLockDoc(doc, e);
}
});
}
catch (Exception e) {
failLockDoc(doc, e);
return;
}
}
if (!sys.async || !lockIt)
afterLockDoc(path, amend);
}
protected void refreshDocFailed (Attachment doc, Exception e) {
gui.alert("Can't access " + doc.getName(), "Reason: " + e.getMessage(),
GuiUtil.getMainFrame(), $warning);
refreshAttachments();
}
protected void afterLockDoc (string path, symbol amend) {
if (amend == $makeReadOnly)
Util.setFilePermission(path, $readOnly);
else if (amend == $makeWriteable)
Util.setFilePermission(path, $writeable);
attachTable.refresh = true;
Desktop.open(path);
}
protected void failLockDoc (Attachment doc, Exception e) {
gui.alert("Can't lock " + doc.getName(), "Reason: " + e.getMessage(),
GuiUtil.getMainFrame(), $waring);
refreshAttachments();
return;
}
protected boolean canEditDoc () {
return !sys.flash && editableDoc();
}
protected boolean editableDoc () {
Attachment doc = getSelectedAttachment();
Quest Sample Application
315
int id @= doc?.lockedBy();
return editable && doc != null && (id == null || id == Quest.singleton.getUserId());
}
protected boolean canSaveDoc () {
Attachment doc = getSelectedAttachment();
int id @= doc?.lockedBy();
return editable && doc != null && id == Quest.singleton.getUserId();
}
protected void saveDoc () {
Attachment doc = getSelectedAttachment();
saveAndRefresh(doc, true);
unlockDoc(false);
}
protected boolean canUnlockDoc () {
return canSaveDoc();
}
protected void unlockDoc (boolean warn) {
if (warn)
gui.alert("Are you sure?", $"Unlock {getSelectedAttachmentName()}?\nUnlocking will disca
rd any changes made to the document.'", GuiUtil.getMainFrame(), $question, $unlockDocContinue);
else
unlockDocContinue($default);
}
protected void unlockDocContinue (symbol answer) {
if (answer == $no || answer == $cancel)
return;
Attachment doc = getSelectedAttachment();
try {
doc.unlock(Quest.singleton.getUserId(), !sys.async ? null : new Callback() {
public void completed (vague data) {
refreshAttachments();
}
public void failed (Exception e) {
failUnlockDoc(doc, e);
}
});
}
catch (Exception e) {
failUnlockDoc(doc, e);
return;
}
if (!sys.async)
refreshAttachments();
}
protected void failUnlockDoc (Attachment doc, Exception e) {
gui.alert("Can't unlock " + doc.getName(), "Reason: " + e.getMessage(),
GuiUtil.getMainFrame(), $warning);
}
protected boolean canDeleteDoc () {
return editableDoc();
}
protected void deleteDoc () {
gui.alert("Are you sure?", $"Permanently delete {getSelectedAttachmentName()}?",
GuiUtil.getMainFrame(), $question, $deleteDocContinue);
}
protected void deleteDocContinue (symbol answer) {
316
JavaGram Agile Development
if (answer == $no || answer == $cancel)
return;
Attachment doc = getSelectedAttachment();
try {
doc.delete(!sys.async ? null : new Callback() {
public void completed (vague data) {
refreshAttachments();
}
public void failed (Exception e) {
deleteDocFailed(doc, e);
}
});
}
catch (Exception e) {
deleteDocFailed(doc, e);
}
if (!sys.async)
refreshAttachments();
}
protected void deleteDocFailed (Attachment doc, Exception e) {
gui.alert("Can't delete " + doc.getName(), "Reason: " + e.getMessage(),
GuiUtil.getMainFrame(), $warning);
}
protected void refreshAttachments () {
Issue issue @= getContent();
Attachment doc = getSelectedAttachment();
vector<Attachment> vec @= Attachment.find(Attachment, $issueId, issue.getId())
?> sys.async
-> {
issue.setAttach(vec);
attachTable.data = vec;
attachTable.refresh = true;
if (doc != null) {
// Select what was selected before refresh:
for (int i = 0, n = sys.length(vec); i < n; ++i) {
if (doc.getId() == vec[i].getId()) {
attachTable.select = i;
break;
}
}
}
};
}
protected void saveAndRefresh (Attachment doc, boolean existing) {
try {
doc.save(existing);
}
catch (Exception e) {
gui.alert("Can't save " + doc.getName(), "Reason: " + e.getMessage(),
GuiUtil.getMainFrame(), $warning);
}
refreshAttachments();
}
protected boolean actionAllowed (symbol action) {
Quest Sample Application
317
Issue issue @= getContent();
return issue != null
&& sys.find(Rules.singleton.stateToActions(issue.getState()), action) != null;
}
protected boolean canReopen () {
Issue issue @= getContent();
return issue != null && issue.getState() == "Closed"
&& Quest.singleton.getCurrUser().isAdminUser();
}
protected void submit () {
if (onModified())
doAction($submit);
}
protected void assign () {
if (onModified()) {
int userId = AssignDialog.singleton.show(getContent()@Issue.getAssignedTo()@int,
!sys.async ? null : new Callback(){
public void completed (vague data) {
afterAssign(data@int);
}
});
if (!sys.async)
afterAssign(userId);
}
}
protected void afterAssign (int userId) {
if (userId != null) {
getContent()@Issue.setAssignedTo(userId);
string assignedToName @= User.getUserInfo(true, userId)[$name];
assignedToField.value = assignedToName;
doAction($assign, "Assigned to " + assignedToName);
}
}
protected void open () {
if (onModified())
doAction($open);
}
protected void resolve () {
if (onModified()) {
string resolution = ResolveDialog.singleton.show(!sys.async ? null : new Callback(){
public void completed (vague data) {
afterResolve(data@string);
}
});
if (!sys.async)
afterResolve(resolution);
}
}
protected void afterResolve (string resolution) {
if (resolution != null)
doAction($resolve, "Resolved: " + resolution);
}
protected void reject () {
if (onModified()) {
string reason = RejectDialog.singleton.show(!sys.async ? null : new Callback(){
public void completed (vague data) {
318
JavaGram Agile Development
afterReject(data@string);
}
});
if (!sys.async)
afterReject(reason);
}
}
protected void afterReject (string reason) {
if (reason != null)
doAction($reject, "Reason: " + reason);
}
protected void close () {
if (onModified())
doAction($close);
}
protected void reopen () {
if (onModified())
doAction($open, "Reopened");
}
protected boolean onModified () {
boolean res = [email protected]();
return res && getContent()@Issue.lockedBy() == Quest.singleton.getUserId();
}
protected void doAction (symbol action, string note = null) {
Issue issue @= getContent();
string oldState = issue.getState();
string newState = Rules.singleton.actionToState(action);
if (newState == null)
newState = oldState;
issue.setState(newState);
stateField.value = newState;
HistoryDetails hd = object(HistoryDetails
, issueId => issue.getId()@int
, actorId => Quest.singleton.getUserId()@int
, actDate => sys.date()
, action => action
, oldState => oldState
, newState => newState
, note => note
);
History hist = new History(hd).updateVolatiles();
issue.addHistory(hist);
AppTree.singleton.persistNodeCmd(false);
}
}
</jag>
8.3.4.3 AssignDialog
<jag domain="quest/gui/issue">
<load>
"lib/lang/Common"
"lib/gui/GuiUtil"
"quest/bom/User"
</load>
singleton class AssignDialog {
string oldUserName;
Quest Sample Application
319
int assignedUserId;
Callback cb;
<Dialog dialog parent={GuiUtil.getMainFrame()} center={GuiUtil.getMainFrame()} modal=true
fixed=true width=200 height=90 title="Assign Issue">
<Layout.border/>
<Panel lay=$center>
<Layout.gridBag/>
<Lay row=0 col=0 margin=2 align=$east weight=0.0>
<Label title="Assign To"/>
</Lay>
<Lay row=0 col=1 margin=2 fill=$horizontal weight=1.0>
<Combo idCombo model=userModel event=eventHandler/>
</Lay>
</Panel>
<Panel lay=$south>
<Button assignButn title="Assign" image={sys.use("lib/gifs/Assign.gif")}
action={assign()} enable={canAssign()}/>
<Button title="Cancel" image={sys.use("lib/gifs/Cancel.gif")} action={cancel()}/>
</Panel>
</Dialog>
public int show (int userId, Callback cb = null) {
this.cb = cb;
oldUserName @= User.getUserInfo(false, userId)[$name];
idCombo.value = User.getUserInfo(true, userId)[$name];
dialog.enter = assignButn;
gui.maintain(dialog);
dialog.show = true;
return assignedUserId;
}
protected boolean canAssign () {
return idCombo.value != "" && idCombo.value != oldUserName;
}
protected void assign () {
assignedUserId @= User.userModel(true, $id, idCombo.select);
dialog.show = false;
if (cb != null)
cb.completed(assignedUserId);
}
protected void cancel () {
assignedUserId = null;
dialog.show = false;
}
protected void eventHandler (native comp, symbol event) {
gui.maintain(dialog);
}
protected vague userModel (native combo, symbol cmd, int idx) {
return User.userModel(true, cmd, idx);
}
}
</jag>
8.3.4.4 ResolveDialog
<jag domain="quest/gui/issue">
<load>
320
JavaGram Agile Development
"lib/lang/Common"
"lib/gui/GuiUtil"
</load>
singleton class ResolveDialog {
string resolution;
Callback cb;
<Dialog dialog parent={GuiUtil.getMainFrame()} center={GuiUtil.getMainFrame()} modal=true
fixed=true width=400 height=200 title="Resolve Issue">
<Layout.border/>
<Panel lay=$center title="Resolution">
<Layout.border/>
<Pane.scroll lay=$center>
<Area.text resTextArea lineWrap=true/>
</Pane.scroll>
</Panel>
<Panel lay=$south>
<Button resolveButn title="Resolve" image={sys.use("lib/gifs/Apply.gif")}
action={resolve()}/>
<Button title="Cancel" image={sys.use("lib/gifs/Cancel.gif")} action={cancel()}/>
</Panel>
</Dialog>
public string show (Callback cb = null) {
this.cb = cb;
dialog.enter = resolveButn;
resTextArea.value = "";
dialog.show = true;
return resolution;
}
protected void resolve () {
resolution @= resTextArea.value;
dialog.show = false;
if (cb != null)
cb.completed(resolution);
}
protected void cancel () {
resolution = null;
dialog.show = false;
}
}
</jag>
8.3.4.5 RejectDialog
<jag domain="quest/gui/issue">
<load>
"lib/lang/Common"
"lib/gui/GuiUtil"
</load>
singleton class RejectDialog {
string reason;
Callback cb;
<Dialog dialog parent={GuiUtil.getMainFrame()} center={GuiUtil.getMainFrame()} modal=true
fixed=true width=400 height=200 title="Reject Resolution">
<Layout.border/>
<Panel lay=$center title="Rejection Reason">
Quest Sample Application
321
<Layout.border/>
<Pane.scroll lay=$center>
<Area.text rejTextArea lineWrap=true/>
</Pane.scroll>
</Panel>
<Panel lay=$south>
<Button rejectButn title="Reject" image={sys.use("lib/gifs/Reject.gif")}
action={reject()}/>
<Button title="Cancel" image={sys.use("lib/gifs/Cancel.gif")} action={cancel()}/>
</Panel>
</Dialog>
public string show (Callback cb = null) {
this.cb = cb;
dialog.enter = rejectButn;
rejTextArea.value = "";
dialog.show = true;
return reason;
}
protected void reject () {
reason @= rejTextArea.value;
dialog.show = false;
if (cb != null)
cb.completed(reason);
}
protected void cancel () {
reason = null;
dialog.show = false;
}
}
</jag>
8.3.5 Setting
8.3.5.1 PickListPanel
<jag domain="quest/gui/setting">
<load>
"lib/lang/Common"
"lib/bom/Object"
"lib/bom/PickList"
"lib/gui/Table"
"lib/gui/GuiUtil"
"lib/gui/PickListMgr"
</load>
singleton class PickListPanel extends Table {
static final vector<map> tableFormat = [
[$key=>$editable, $title=>"Editable", $width=>10, $align=>$center]
,[$key=>$name, $title=>"Name", $width=>100, $align=>$west]
,[$key=>$count, $title=>"#Items", $width=>20, $align=>$center]
,[$key=>$description, $title=>"Description", $width=>200, $align=>$west]
];
<Panel view title="Pick Lists">
<Layout.border/>
<Pane.scroll lay=$center>
<Indirect ref={table} key=$plVec/>
</Pane>
322
JavaGram Agile Development
<Indirect ref={(new TableButtons(this, [$details])).view} lay=$east/>
</Panel>
public PickListPanel () {
super(tableFormat);
}
protected vague formatCell (vague obj, symbol key) {
switch (key) {
case $editable: return sys.get(obj, key) == true ? GuiUtil.TICK_STR : "";
case $count: return sys.length(sys.get(obj, $items));
default: return sys.get(obj, key);
}
}
public boolean detailsItemCmd (boolean maintain) {
if (maintain) {
return hasSelection();
} else {
vague item = getSelectedItem();
if (item != null)
sys.call(Util.getSingleton("lib/gui/PickListMgr"), $show, item);
}
return true;
}
protected Object drillRow (Object bo) {
PickListMgr.singleton.show(bo@PickList);
refresh();
return bo;
}
}
</jag>
8.3.5.2 LogPanel
<jag domain="quest/gui/setting">
<load>
"lib/gui/Screen"
</load>
singleton class LogPanel extends Screen {
<Font monoFont name="Courier" size=11/>
<Panel view title="Quest Output Log">
<Layout.border/>
<Panel lay=$north>
<Layout.flow/>
<Button title="Clear" image={sys.use("lib/gifs/Clear.gif")} action={logText.value=""}/>
</Panel>
<Pane.scroll>
<Area.text logText tab=4/>
</Pane.scroll>
</Panel>
public LogPanel () {
logText.font = monoFont;
}
public native getTextArea () {
return logText;
}
}
</jag>
Quest Sample Application
323
8.3.6 Miscellaneous
8.3.6.1 AboutDialog
<jag domain="quest/gui/issue">
<load>
"lib/gui/GuiUtil"
</load>
singleton class AboutDialog {
string resolution;
<Dialog dialog parent={GuiUtil.getMainFrame()} center={GuiUtil.getMainFrame()} modal=true
fixed=true title="About Quest" width=350 height=160>
<Layout.border/>
<Panel lay=$north>
<Label align=$center image={sys.use("quest/doc/help/gifs/Quest.gif")}/>
</Panel>
<Panel lay=$center>
<Layout.grid rows=3/>
<Label align=$center title="Sample Application"/>
<Label align=$center title="Developed using JavaGram Technology"/>
<Label align=$center title="Copyright © 2010 PragSoft Corporation (www.pragsoft.com)"/>
</Panel>
<Panel lay=$south>
<Button resolveButn title="OK" image={sys.use("lib/gifs/Apply.gif")}
action={dialog.show=false}/>
</Panel>
</Dialog>
public void show () {
dialog.show = true;
}
}
</jag>
8.3.6.2 HtmlPanel
<jag domain="quest/gui/misc">
<load>
"lib/bom/Document"
"lib/gui/Html"
"quest/gui/AppTree"
</load>
singleton class HtmlPanel extends HtmlScreen {
public HtmlPanel () {
super("quest");
}
protected void doFileRef (string path, boolean isLocal) {
super.doFileRef(path, isLocal);
AppTree.singleton.showHelpPage(Document.getByPath(path));
}
}
</jag>
8.3.7 Configuration
// Quest app-server configuration.
[ $download=>[ $default=>["jag"=>$private, "*"=>$public]
324
JavaGram Agile Development
,
,
,
,
,
,
,
,
,
, $cache=>"svrCache/"
]
$upload=>[$default=>["*"=>$public]]
$security=>[$keystore=>"ssl/JAG.jks", $password=>"changeit", $plainPortInc=>100]
$policy=>"flash/crossdomain.xml"
$mode=>$async
$partition => [ $cache => "parts"
, $exclude => [".svn/*", "*.jagzip"]
, "QuestPart" => [("quest/", $recursive, "*.jag", "*.html", "*.gif")
,("lib/", $recursive, "*.jag", "*.gif")]
]
$session=>[$timeout=>300, $error=>10] // 5 minutes timeout
$log=>[$timeout=>86400]
// 1 day per log file
$zip=>[$min=>128]
$database=>[ $defPars=>[ $user=>"root"
, $password=>"password"
, $timeout=>[$login=>30, $query=>20]
, $driver=>"com.mysql.jdbc.Driver"
]
, $mysqlDb=>[ $defaults=>$defPars
, $name=>"MySql"
, $url=>"jdbc:mysql://localhost/mysql"
]
, $questDb=>[ $defaults=>$defPars
, $name=>"Quest"
, $url=>"jdbc:mysql://localhost/quest"
]
]
]
Quest Sample Application
325
9 Extensions
Two parts of JavaGram have been intentionally designed to be user extensible – the GUI
markup and the text markup notations. Internally, JavaGram uses reflection to determine
what’s available in support of these notations. Therefore, so long as your extension
follows the coding guidelines described in this chapter and its JAR file made accessible,
JavaGram will support it in exactly the same way as what’s included in JAG. JavaGram
also allows you to create ad-hoc extensions (anything that can be coded in Java) and
access them via the sys.java() method.
9.1 Introduction
When working on a JavaGram project, you might come across rare situations that are best
implemented in Java. You can do this by writing Java code that augments JAG. To do
this, you need to have some knowledge of the design of JAG and the classes that it
comprises. This section outlines the JAG Java classes that you would need to interact
with. The rest of the chapter describes actual examples of how extensions are written.
9.1.1 Parse Tree
When a JavaGram script is parsed, it’s converted to a parse tree. Each parsed construct is
represented by a node (which is a Java class instance) in the parse tree. For example, a
string literal is represented by a StrNode, and an addition is represented by an
AddExprNode. The various node types form an inheritance hierarchy, the superclass for
which is AbsNode. Here is the UML diagram for a few sample nodes.
Location
Comparable
AbsNode
RealNode
right
left
DateNode
init
DebugNode
AddExprNode
StrNode
VarNode
Insightable
Location is a Java interface that allows JavaGram to record the physical location of a
node in its parent script. Comparable is another interface that allows nodes to be compared
326
JavaGram Agile Development
for equality. Every node is ultimately derived from the abstract class AbsNode. Therefore,
every node can be treated as an instance of AbsNode. Note, for example, how AddExprNode
treats its left and right operands as AbsNode instances. Primitive literals (such as integer,
real, string, etc) are directly derived from AbsNode. All other nodes are derived from
another abstract class called DebugNode which is itself a subclass of AbsNode. DebugNode
captures debugging information so that JADE can exercise break points on them.
There are over 120 node classes defined in the jag.parse.node package. To write an
extension, you need to know about only a few of these. The ones you’re most likely to
use deal with the built-in types, constants, and identifiers:

TrueNode represents the constant true. This is a singleton class whose instance is
accessed as TrueNode.self.

FalseNode represents the constant false. This is a singleton class whose instance is
accessed as FalseNode.self.

NullNode represents the constant null. This is a singleton class whose instance is
accessed as NullNode.self.

CharNode represents a character literal. CharNode instances are cached, so you’re not
allowed to instantiate this class – use CharNode.create() instead.

IntNode represents an integer literal. IntNode instances in the range -128 to 127 are
cached, so you’re not allowed to instantiate this class – use IntNode.create() instead.

RealNode represents a real literal.

StrNode represents a string literal.

SymNode represents a symbol literal. SymNode instances are cached to ensure uniqueness,
so you’re not allowed to instantiate this class – use SymNode.define() and
SymNode.lookup() instead.

DateNode represents a date literal.

PairNode represents the building block of a list.

VectorNode represents a vector.

MapNode represents a map.

IdNode represents an identifier.
The jag.parse package contains classes that manage the parsing of JavaGram code. The
most important classes in this package are:

Lexer performs lexical analysis (i.e., tokenizes JavaGram source).

Parser converts the token stream produces by Lexer into a parse tree.

ProgramScope manages the scope of all identifiers.
Extensions
327
Two important methods that every node supports are:
public AbsNode analyze (Parser parser, long redoing) throws JagThrow {
// Performs semantic analysis of the node
}
public AbsNode eval (Env env) throws JagThrow {
// Evaluates the node during program execution
}
The JavaGram Analyzer analyzes a script by recursively calling analyze() on its parse
tree. Likewise, the JavaGram Evaluator executes a script by recursively calling eval() on
its parse tree.
All exceptions in JAG are represented by JagThrow, which records an exception and its
physical location. Therefore, most methods are defined to throw this exception.
9.1.2 Types
JavaGram types are defined as instances of the jag.parse.node.TypeNode class. Primitive
and pseudo types are defined as constants in this class:

TypeNode.booleanType represents boolean

TypeNode.charType represents char

TypeNode.dateType represents date

TypeNode.intType represents int

TypeNode.listType represents list

TypeNode.nativeType represents native

TypeNode.objectType represents object

TypeNode.realType represents real

TypeNode.streamType represents stream

TypeNode.stringType represents string

TypeNode.symbolType represents symbol

TypeNode.vagueType represents vague

TypeNode.voidType represents void
Because vector types can be parameterized, they’re represented by instances of
jag.parse.node.VectorTypeNode which subclasses TypeNode. You can’t instantiate this
class directly; instead, you should use its create() method, which caches the new type.
For example, VectorTypeNode.create(null, TypeNode.listType) defines a vector of lists,
or vector<list>. Within this class, two vector types are predefined:
328
JavaGram Agile Development

VectorTypeNode.defaultType represents vector<vague> which is the same as vector.

VectorTypeNode.strVecType represents vector<string>
Similarly, map types are represented by instances of jag.parse.node.MapTypeNode which
sublcasses TypeNode. For example, MapTypeNode.create(null, TypeNode.symbolType,
TypeNode.stringType) defines map<symbol, string>. The default map type is predefined
in this class as MapTypeNode.defaultType, which represents map<vague, vague> which is
the same as map.
A user-defined type is represented by an instance of jag.parse.node.NamedTypeNode,
which is eventually resolved by the analyzer to an instance of jag.parse.node.ClassNode,
which is a subclass of NamedTypeNode.
9.1.3 Objects
All class instances (objects) are represented by the jag.run.obj.ClassObj abstract class.
AbsNode
Location
fields
NamedTypeNode
type
ProxyObj
ClassObj
obj
ActualObj
RemoteObj
LiteralObj
ClassObj has a number of subclasses that represent different ‘flavors’ of objects:

ActualObj represents real objects that have representation in memory (i.e., local
objects created using the new operator).

LiteralObj represents literal objects (i.e., objects that are directly expressed in code).

RemoteObj represents instances of remote classes.

ProxyObj represents the client-side proxies of remote objects.
To create a JavaGram object in Java code, use the ActualObj.create() method, whose
signature is:
public static ActualObj create (Env env, ClassNode klass)
This method examines klass to determine whether the instance should be local or remote.
Extensions
329
9.1.4 IO
All JavaGram streams (be they file, memory, or channel based) are represented by the
jag.io.JagStream class, which is a subclass of AbsNode. Memory based streams (created
using sys.buffer()) also use StrWriter and StrReader, which in turn use StrBuffer to
maintain a memory representation of the IO data.
AbsNode
PrintWriter
BufferedReader
JagStream
StrWriter
StrReader
buf
buf
StrBuffer
To read from an input stream, you first use the JagStream.getIn() method, which returns
the stream’s Parser instance. You then use one of the parser methods for reading. For
example:
// Assuming that ps is an input JagStream:
AbsNode expr = ps.getIn().parseAndAnalyzeExpr(null);
Every parse tree node has a pp() method for pretty printing that node to a PrintWriter,
the signature of which is:
public void pp (Env env, int margin, PrintWriter to, int flags);
To write to an output stream, you first use the JagStream.getOut() method, which returns
the stream’s PrintWriter instance. You then pass the latter to the pp() method of the
expression you want to output. Pass 0 for margin, and any combination of flags defined in
AbsNode for flags. For example:
expr.pp(env, 0, ps.getOut(),AbsNode.DETAILED_FLAG | AbsNode.LINE_BREAK_FLAG);
9.1.5 Environment
The class jag.run.Env provides a complete runtime environment for each running thread.
You would notice that a lot of JAG methods take an Env type argument. This is because
they need to have access to things like the thread’s stack, IO streams, and so on. Env also
provides methods for reporting runtime errors and warnings.
9.2 Writing a GUI Extension
This section demonstrates the process of writing a JavaGram GUI extension in Java. The
new element we’ll be creating here is called <MemoryBar>, whose aim is to provide a
330
JavaGram Agile Development
visual representation of the amount of memory consumed against the total amount of
memory allocated to the current process.
9.2.1 Usage
To illustrate the appearance and behavior of the new element, we’ve written a sample
application that uses it.
<jag domain="doc/code/chap9">
<load>
"lib/gui/GuiApp"
</load>
singleton class ExtDemo extends GuiApp {
<App app lookAndFeel=$windows>
<Frame frame title="Demo App" width=300 height=200 event=frameHandler>
<Panel>
<Layout.border/>
<Panel lay=$south>
<MemoryBar allowGC=true delay=2000/>
</Panel>
</Panel>
</Frame>
</App>
public ExtDemo () {
super(frame);
}
protected void frameHandler (native comp, symbol event) {
if (event == $close)
exit();
}
public static void main () {
ExtDemo.singleton.run();
}
}
</jag>
This application is similar to our earliest demo application presented in Chapter 4. It
displays a frame, at the bottom of which we have a <MemoryBar>. Running the application
displays the following frame.
Extensions
331
The memory bar supports two properties (in addition to the normal properties supported
by all visual elements). When set to true, the allowGC property causes a small trashcan
button to be displayed next to the memory bar. Pressing this button invokes garbage
collection. The delay property specifies (in milliseconds) how often the memory bar
should be updated.
9.2.2 Naming Conventions
Every GUI element is implemented by a Java class in the jag.gui.tags package. The
class name must be the same as the element name, plus a Gui prefix. So, for example, the
qualified Java class name for <MemoryBar> would be jag.gui.tags.GuiMemoryBar. Where
an element name contains a period, this should be replaced by an underscore (e.g., the
Java class name for <Field.text> is jag.gui.tags.GuiField_text).
9.2.3 Implementation
To implement the new GUI element, you must subclass an existing Java class defined in
JAG.jar. You have three options:

Subclass jag.gui.tags.GuiAbcComp. This is recommended for non-visual elements
(e.g., <Worker>).

Subclass jag.gui.tags.GuiComp. This is recommended for visual elements that need to
be developed from the grounds up.

Subclass the class of an existing GUI element. This is recommended for elements that
build on top of existing elements (e.g., to devise a new type of text field, you can
subclass jag.gui.tags.GuiField_text).
We’ve used the second approach for <MemoryBar>, resulting in the following Java class.
package jag.gui.tags;
import
import
import
import
import
import
import
import
import
import
java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
jag.gui.GuiUtil;
jag.misc.JagThrow;
jag.parse.Location;
jag.parse.node.*;
jag.run.Env;
jag.run.obj.ClassObj;
public class GuiMemoryBar extends GuiComp {
static final Color GREY = new Color(120, 120, 120);
static final Color GREEN = new Color(0, 205, 0);
static final Color RED = new Color(205, 0, 0);
332
JavaGram Agile Development
static
static
static
static
final
final
final
final
Color AMBER = new Color(250, 128, 23);
String ALLOW_GC = "allowGC";
String DELAY = "delay";
int DEFAULT_DELAY = 5000;
// 5 seconds
JButton butn = new JButton(new
ImageIcon(GuiMemoryBar.class.getResource("Trash.gif")));
JProgressBar bar = new JProgressBar();
javax.swing.Timer timer = new javax.swing.Timer(DEFAULT_DELAY, null);
public GuiMemoryBar (Location loc, ClassObj dis) {
super(loc, dis, new JPanel());
getPanel().add(butn);
getPanel().add(bar);
bar.setStringPainted(true);
bar.setBackground(GREY);
updateMemory();
butn.setVisible(false);
butn.setFocusPainted(false);
Dimension d = new Dimension(20, 20);
butn.setPreferredSize(d);
butn.setMaximumSize(d);
bar.setPreferredSize(new Dimension(90, 20));
bar.setMinimumSize(new Dimension(80, 20));
butn.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent e) {
gc();
}
});
timer.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent e) {
updateMemory();
}
});
timer.start();
}
public JPanel getPanel () {
return (JPanel) nativeVal;
}
public static Map propKinds;
public static Map getPropKinds () {
if (propKinds == null) {
propKinds = new TreeMap();
propKinds.putAll(GuiComp.getPropKinds());
propKinds.put(DELAY, TypeNode.intType);
propKinds.put(ALLOW_GC, TypeNode.booleanType);
}
return propKinds;
Extensions
333
}
public static String[] getPropValues (String prop) {
if (prop.equals(ALLOW_GC))
return GuiUtil.falseAndTrue;
return GuiComp.getPropValues(prop);
}
public AbsNode doProp (Env env, Location location, String prop, AbsNode val)
throws JagThrow {
if (prop.equals(ALLOW_GC)) {
if (val != null)
butn.setVisible(val == TrueNode.self);
else
return AbsNode.asTrueFalse(butn.isVisible());
} else if (prop.equals(DELAY)) {
if (val != null)
timer.setDelay(val.intValue(env, location));
else
return IntNode.create(timer.getDelay());
} else
super.doProp(env, location, prop, val);
return TrueNode.self;
}
public AbsNode getValue () {
return IntNode.create(bar.getValue());
}
public void setValue (Env env, Location location, AbsNode value) throws JagThrow {
// Not needed, as it's controlled internally
}
public void refresh () {
updateMemory();
super.refresh();
}
protected void updateMemory () {
int totalMem = (int) (Runtime.getRuntime().totalMemory() / 1000000);
int freeMem = (int) (Runtime.getRuntime().freeMemory() / 1000000);
bar.setString(totalMem - freeMem + "M of " + totalMem + "M");
bar.setToolTipText("Memory: " + (totalMem - freeMem) + "M of allocated "
+ totalMem + "M used");
bar.setMaximum(totalMem);
bar.setValue(totalMem - freeMem);
double freeRatio = ((double)freeMem) / totalMem;
Color fg = freeRatio >= 0.5 ? GREEN : (freeRatio >= 0.25 ? AMBER : RED);
bar.setForeground(fg);
}
334
JavaGram Agile Development
protected void gc () {
bar.grabFocus();
Runtime.getRuntime().gc();
updateMemory();
}
}
This class creates a button and a progress bar, and wraps these in a panel (see the
constructor). It also sets up a timer for periodically refreshing the progress bar. The action
handler for the button invokes the gc() method to do garbage collection. The action
handler for the timer invokes updateMemory(), which displays the amount of memory used
versus the total allocated, and uses green/amber/red color coding for the used portion.
All GUI implementation classes must include the following pattern:
public static Map propKinds;
public static Map getPropKinds () {
//...
}
public static String[] getPropValues (String prop) {
//...
}
getPropKinds() is internally called by JavaGram to discover the type of each of the
component’s properties. getPropValues() is called by JADE’s code insight to discover the
list of possible values for those properties that are enumerations (e.g., possible values for
the lay property).
doProp() handles each of the component’s properties, and calls super.doProp() to handle
properties inherited by the component. For each property, if val is non-null then the
property is being set to that value. Otherwise, we’re getting the property’s value.
For container components (not this example), you also need to override the doPart()
method, whose signature is shown below. This method should add the component
denoted by part to the container when add is true, and remove it otherwise.
public AbsNode doPart (Env env, Location location, boolean add, AbsNode part)
throws JagThrow;
For components that support a value property, you also need to override the getValue()
and setValue() methods, as we’ve done here (the latter is not really needed in this case
and is only included to illustrates its signature). These methods are internally called by
JavGram during data binding.
Finally, we’ve also overridden refresh() which is internally called when the component
needs to be refreshed. This is just for completeness and not really needed in this example.
Extensions
335
9.3 Writing a Text Extension
To implement a new text element, you must subclass an existing Java class defined in
JAG.jar. You have two options:

Subclass jag.text.handlers.HandlerText. This is recommended for text elements that
need to be defined from the grounds up.

Subclass the class of an existing text element. This is recommended for elements that
build on top of existing elements (e.g., to devise a new type of SQL text element, you
can subclass jag.text.handlers.HandlerText_sql or one of its subclasses).
Rather than showing a new example here, we’ll present the code for HandlerText_sql
which implements <text.sql>.
package jag.text.handlers;
import
import
import
import
import
import
import
import
java.util.*;
jag.parse.node.*;
jag.parse.Parser;
jag.gui.GuiUtil;
jag.misc.JagThrow;
jag.Globals;
jag.sql.*;
jag.run.Env;
public class HandlerText_sql extends HandlerText {
public HandlerText_sql (TextDefNode def) {
super(def);
}
public void analyzeProp (Parser parser, String prop, AbsNode value, long redoing)
throws JagThrow {
if (parser.isFake())
return;
if (prop.equals(Globals.ESCAPE)) {
if (value.getType() != TypeNode.stringType)
parser.error(def, "'escape' property must have a string value: " + value);
} else {
super.analyzeProp(parser, prop, value, redoing);
}
}
public static Map propKinds;
public static Map getPropKinds () {
if (propKinds == null) {
propKinds = new TreeMap();
propKinds.putAll(HandlerText.getPropKinds());
propKinds.put(Globals.ESCAPE, TypeNode.stringType);
}
336
JavaGram Agile Development
return propKinds;
}
public static String[] getPropValues (String prop) {
return HandlerText.getPropValues(prop);
}
public String handlePart (Env env, AbsNode part) throws JagThrow {
AbsNode value = part.eval(env);
String svalue = value.toString();
if (part instanceof IdNode) {
String pn = part.toString();
throw JagThrow.warning(part, "substitution of {" + pn +
"} not allowed in this context, use {`" + pn + "} or {=" + pn + "}");
}
if (part instanceof SqlCurlyNode || value instanceof DelayedStrNode)
return svalue; // these classes have already handled escaping
return value instanceof StrNode
? SqlUtil.escapeSQLFragment(svalue, true)
: svalue;
}
}
The analyzeProp() method is overridden to process properties additional for this element.
There is only one additional property here (escape), the rest are processed by calling the
analyzeProp() method of the superclass. The call parser.isFake() returns true when the
parser is processing compiled code, in which case we don’t need to analyze the properties
because they would have already been analyzed during compilation.
The getPropKinds() and getPropValues() methods are overridden for the same reasons as
explained in Section 9.2 for GUI elements.
When a text block is processed by the parser, it’s broken into fragments that comprise the
text, separating literal text from spliced expressions (i.e., things enclosed in braces). The
handlePart() method is overridden to process these fragments, each of which is denoted
by the part parameter.
If your text method returns a non-string type (not in this example), you will also need to
override analyzeReturnType(). For example, <text.sql.update> returns an integer, so it
overrides this method as follows.
public void analyzeReturnType (Parser parser) throws JagThrow {
if (def.getType() != TypeNode.intType)
parser.error(def, "int return type expected: " + def.getType());
}
Extensions
337
9.4 Writing an Ad-hoc Extension
The final type of extension to be discussed here is an ad-hoc one – anything that for any
reason you prefer to implement in Java. As an example, suppose that you need the
functionality to ping servers. This can be easily implemented as a Java class
package jag.misc;
import java.io.*;
public class Ping {
public static String ping (String ip) {
StringBuilder sb = new StringBuilder();
try {
InputStream is = Runtime.getRuntime().exec("ping " + ip).getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = in.readLine()) != null)
sb.append(line);
in.close();
}
catch (IOException e) {
return null;
}
return sb.toString();
}
}
The Ping.ping() method takes the IP address (or domain name) of a server as argument,
attempts to ping it by sending the command ping ip to it, and returns the result as a
string.
To call Ping.ping() from JavaGram, we simply use the sys.java() method. This method
allows any accessible Java method (even constructors) to be called. Here is a sample
JavaGram program that does just that. It displays a text field for entering an IP address, a
button to initiate the ping, and a text area for displaying the result of the ping.
<jag domain="doc/code/chap9">
<load>
"lib/gui/GuiApp"
</load>
singleton class PingTest extends GuiApp {
<App app lookAndFeel=$windows>
<Frame frame title="Ping Test" width=300 height=200 event=frameHandler>
<Panel>
<Layout.border/>
<Panel lay=$north>
<Layout.border/>
<Field.text ipField lay=$center/>
<Button title="Ping" lay=$east action={ping(ipField.value@string)}/>
338
JavaGram Agile Development
</Panel>
<Pane.scroll lay=$center>
<Area.text text/>
</Pane.scroll>
</Panel>
</Frame>
</App>
public PingTest () {
super(frame);
//sys.jload("C:/eclipse/ws/SampleExt/JagExt.jar");
}
protected void ping (string ip) {
string res @= sys.java("jag.misc.Ping", null, "ping", list($string, ip));
text.value = res;
}
protected void frameHandler (native comp, symbol event) {
if (event == $close)
exit();
}
public static void main () {
PingTest.singleton.run();
}
}
</jag>
Note the sys.java() call in the PingTest.ping() method. Because the Java method is
static, the first argument specifies its qualified class name (otherwise, this argument
should be a valid Java object reference). The second argument specifies the required
return type. We’ve passed null here, which causes the Java return type to be
automatically converted to its corresponding JavaGram type. The third argument is the
method name, and the last argument is a list or vector of argument types and values.
When you run this application, it displays the following frame (where we’ve entered an
IP address and pressed the Ping button).
Extensions
339
9.5 Packaging Your Extension
Before using your extension, you must package it as a JAR file and make that JAR file
available to JavaGram. The easiest way to do this is to use a Java IDE (such as Eclipse)
to compile and JAR your Java code. Suppose that you’ve done this and produced a JAR
file called JagExt.jar that includes the examples presented in this chapter.
For GUI and text extensions, we recommend that you make the JAR file available to
JADE so that the editor can visually recognize your extensions. For example, the
command
javaw –cp JADE.jar;JagExt.jar jade.Application
will invoke JADE, making the extension available to it. Thereafter, if you type
<MemoryBar> in the IDE, it will be recognized, and code insight will understand the
<MemoryBar> properties.
Similarly, when you run a JavaGram program, you should include the JAR file for your
extensions. For example:
javaw -cp JAG.jar;JagExt.jar jag.gui.Gui -root C:/JavaGram doc/code/chap9/PingTest.jag
Running JavaGram programs within JADE is even easier. Simply, choose Preferences
from the Tools menu, select the Environment/Java entry in the preferences tree, and add
your extension’s JAR file to the list. This will be permanently remembered.
Finally, for ad-hoc extensions only, you can load the JAR file during execution rather
than specifying it as a part of the command line. To do this, use the sys.jload() method
and pass the JAR path to it.
340
JavaGram Agile Development
10
Core Reference
This chapter specifies the core features of JavaGram. Use this chapter to look up topics
once you’ve become familiar with the language.
Throughout this chapter, by load time, we mean when a script is parsed. Because a
compiled script is pre-parsed, by load time we may also mean compile time. Both these
are distinct from runtime, which is when the script is actually executed.
10.1 Comments
JavaGram supports two styles of comments, similar to C++ and Java:

Anything appearing after // until the end of the line is a comment.

Anything appearing between /* and */ is treated as a comment. Such comments can
span across multiple lines but cannot be nested.
All comments are ignored by the JavaGram parser.
10.2 Identifiers
An identifier is an alphanumeric name for something (variables, methods, classes, etc.).
JavaGram rules for identifiers are the same as Java. Every identifier must begin with a
letter (A-Z or a-z) or an underscore, followed by zero or more letters, underscores, or
digits. There is no limit on the length of an identifier. Valid examples are:
name
firstName
first_name
_temp
value2
Invalid examples include:
1stName
total$value
switch
// cannot start with a digit
// contains $
// reserved word (see below)
10.2.1 Reserved Words
The following words are reserved in JavaGram and cannot be used as identifiers.
Core Reference
341
abstract
arg
boolean
break
case
catch
char
class
clocal
const
continue
date
default
delayed
do
else
enum
extends
false
final
finally
for
generated
getable
goto
gui
if
implements
import
in
instanceof
int
interface
list
map
mutual
native
new
null
package
private
protected
public
query
real
remote
return
setable
singleton
slocal
sql
static
stream
string
super
switch
symbol
synchronized
sys
this
throw
throws
transaction
transient
true
try
typeof
vague
valueof
vector
void
volatile
while
Reserved words appearing in italics are not currently in use, but are reserved for future
use.
10.2.2 Qualification
Identifiers may be qualified using the backslash and dot characters. For example, a class
named Client in a domain called crm may be referred to as crm\Client, and a static field
in this class called type may be referred to as crm\Client.type.
In most cases, you don’t need to qualify an identifier, but you must do so when the short
version is ambiguous (i.e., resolves to more than one definition).
10.2.3 Naming Conventions
Although not enforced by the language, the following naming conventions are
recommended for identifiers. JavaGram libraries strictly follow these conventions. By
adopting them, you can improve the consistency of your code, especially when you share
your code with other people.

All constants (i.e., static final fields) should appear in upper case throughout, and
individual words separated by underscores. Examples: PROD_CODE, DEFAULT_VALUE.

All variable names (class fields, local variables, method parameters) and method
names should appear in camel case (i.e., first word in lower case and subsequent
words capitalized). Examples: name, firstName, sendMessage.

All class names should have every word capitalized. Examples: Client, CalcEngine.

The larger the scope of an identifier, the more meaningful its name should be.
Therefore, avoid cryptic class and method names because they have a wide scope
(e.g., TransactionCategory or TxnCategory is preferred to TranCat). Conversely, a local
342
JavaGram Agile Development
variable limited to a small method or block should have a brief name (e.g., ts is more
convenient than timestamp).

Established abbreviation and acronyms are perfectly acceptable (e.g., DOB or dob
instead of dateOfBirth).

Single letter loop variables are perfectly acceptable, provided there isn’t too many of
them to cause confusion (e.g., i or j for an integer, or s for a string).

Use nouns for class and variable names, but verbs for method names.
10.3 Jag Element
A JavaGram script (.jag file) consist of a single jag element, the structure of which is
like this:
<jag ...properties...>
...
</jag>
Inside a jag element, you may have zero or more load elements, class definitions, or
literal data.
As a special case, you can also have a tagless script that just contains a literal, such as a
vector. Tagless scripts serve as data files. If a tagless script is loaded, it’s parsed but never
analyzed. This situation happens in the JavaGram IDE. Analysis is skipped because the
file can’t have <load> elements and therefore can’t resolve any class references.
10.3.1 Jag Element Properties
Element properties must conform to the usual markup syntax, where each property is
specified like this:
name=value
Multiple property definitions must be separated by whitespace (not commas).
You can specify the following properties for a jag element. All jag property values must
be literal constants (expressions are not allowed).
Property Name
domain
Description
Specifies the domain of the script. This is similar to Java package names,
C++ namespaces, or web domain names, except that backslash or slash is
used as the separator instead of dot. A domain name must be specified as a
string (e.g., "app\\calc" or "app/calc") and becomes an implicit prefix
for every class specified in the script. For example, a class named Foo will
have the qualified name app\calc\Foo.
Use domains to organize your code into manageable hierarchies, and to
Core Reference
343
mjv
stage
avoid name conflicts across scripts that use similar class names.
Specifies the Minimum JavaGram Version as a string (e.g., "1.5"). If a
script uses features that are available as of a specific version of JavaGram,
use this property to document the dependency. JavaGram will report an
error if a script is run with a version of JAG that’s less than that specified
by mjv.
Specifies the development stage of a script. This must be one of these
symbols: $dev (for development), $test (for system test), or $prod (for
production.). When not specified, it defaults to $dev. When you run a
program, you can use the –stage runtime option to specify the overall
development stage of the program (defaults to dev).
A program run in $dev stage, accepts scripts in any stage. A program run
in $test stage, requires all scripts to be in $test or $prod stage. A
program run in $prod stage, requires all scripts to be in $prod stage. Any
violation of these rules is reported as error and will terminate execution.
side
Use this property to track the quality of a script during its development
cycle and to avoid, for example, an insufficiently tested script finding its
way to production. When you write a new script, set its stage initially to
$dev. Once it has been unit tested, set its stage to $test. Finally, when it
has been fully system tested and is production-ready, set its stage to $prod.
Specifies the intended client-server side of a script. This must be one these
symbols: $client, $server, or $both. When not specified, it defaults to
$both. The side of a program is determined by how you run it. Programs
run using the class jag.run.Env accept all scripts, regardless of their side.
Programs run using jag.run.svr.Server cannot run scripts that have
$client side. Programs run using jag.gui.Gui cannot run scripts that
have $server side.
A $client side script is not allowed to run on the server-side. Conversely,
a $server side script is not allowed to run on the client-side. Use this
property to avoid inadvertent execution of a script on a side that it’s not
intended for.
Here is an example of using all the above properties.
<jag domain="app/calc" mjv="1.5" stage=$test side=$server>
10.4 Load Element
Use the load element to load one or more scripts statically. The structure of a load
element is like this:
<load ...properties...>
...
</load>
344
JavaGram Agile Development
Inside a load element, you may specify the path of zero or more scripts. Each path must
be specified as a string. Separate multiple paths using whitespace (not commas).
10.4.1 Load Element Properties
You can specify the following properties for a load element. Load property values may
be literal constants or expressions.
Property Name
relative
autoCallMain
source
Description
When true, causes file paths to be treated relative to the runtime root path
(defaults to true). This is the recommended setting for JavaGram
programs, as it simplifies the task of managing directory names. The
client-server mode uses relative paths for scripts downloaded from the
server.
Set this to false on rare occasions when you want to load a file using its
absolute path. A Flash client assumes all paths to be relative.
When true, automatically calls the public static void main() method
of each class (if any). Defaults to false.
Specifies the source of the script(s) to be loaded. Possible values are:
$local, $boot, or a stream. The default value for this property depends on
how the program is run. For a standalone or a server program, it defaults to
$local. For a client program, it defaults to $boot. The latter is equivalent
to the stream connecting the client to the server (i.e., the stream through
which the client boots itself by downloading scripts from the server).
When a non-local source is used, the nominated file is first downloaded
from the server denoted by the source and then loaded into JAG.
Downloading and loading of a script happens only once, regardless of how
many times a load request on it is made, unless the script has been
modified since its last load.
side
It’s rarely necessary to use this property. The default setting is almost
always adequate.
Specifies the intended client-server side of a load. This must be one these
symbols: $client, $server, or $both. When not specified, it defaults to
$both. A $client side load is ignored on the server-side. Conversely, a
$server side load is ignored on the client-side. Use this property to avoid
loading something that’s meaningless for a given side.
10.4.2 Data Types
Every value in JavaGram is a member (instance) of some type. For example, a whole
number is an instance of the integer type. JavaGram provides strong type checking to
ensure that, for example, when a method is called, the argument values match the
corresponding parameter types. Type checking is primarily done at load time. Any
computation that cannot be fully type checked at load time, is type checked at runtime.
Core Reference
345
10.4.2.1
Atomic Types
An atomic type is one whose values cannot be further subdivided. JavaGram supports
eight atomic types:

Boolean (boolean) represents the two values true and false, and is equivalent to the
Java type boolean.

Character (char) represents ASCII and Unicode characters, and is equivalent to the
Java type char.

Integer (int) represents whole numbers, and is equivalent to the Java type long.

Real (real) represents numbers with a fractional part, and is equivalent to the Java
type double.

String (string) represents arbitrary sequences of characters, and is equivalent to the
Java type java.lang.String.

Symbol (symbol) is like string except that multiple instances having the same
representation are stored only once. It is used to build unique names, such as keys for
a map, and has no direct Java equivalent.

Date (date) represents date and time, up to nano seconds, and is equivalent to the Java
type java.sql.Timestamp.

Stream (stream) represents a mechanism for performing IO with respect to a file,
buffer, or channel. It has no direct Java equivalent.
10.4.2.2
Composite Types
Composite types are the opposite of atomic types – they are composed of other values.
JavaGram supports four composite types:

Vector (vector) represents a contiguous sequence of values, which may be atomic or
composite themselves, and is equivalent to the Java type java.util.List. Vectors are
useful for providing random access to data.

Linked list (list) represents a sequence of values, which may be atomic or composite
themselves. Unlike vector, a list is not suitable for random access, but is memory
efficient for representing relatively small collections.

Map (map) represents key-value pairs, where each unique key (must be atomic) is
associated with an arbitrary value (may be atomic or composite), and is equivalent to
the Java type java.util.Map. The internal map implementation uses Java’s
java.util.TreeMap and is therefore sorted in lexicographic key order.

Object (object) represents opaque instances of user-defined types, and is equivalent
to the Java type java.lang.Object.
Vector, lists, and map types are also called container types. Vectors and maps can be
parameterized; lists can’t. Here are some examples:
346
JavaGram Agile Development
vector<string>
vector<vector<int>>
map<symbol, real>
map<symbol, map<date, vector<string>>>
//
//
//
//
vector
vector
map of
map of
of strings
of integer vectors
symbols to reals
symbols to maps of...
JavaGram performs sufficient type checking to ensure that values of these containers
conform to the specified nested types. If you don’t parameterize a container then the
elements are allowed to take any type, which is to say that:

vector is equivalent to vector<vague>

map is equivalent to map<vague, vague>
10.4.2.3
User-defined Types
Each JavaGram class definition establishes a new type, whose members are instances of
that class. Furthermore, all class instances (i.e., objects) can be treated as members of the
opaque type object.
User-defined types can be extended through inheritance. Unlike Java, JavaGram supports
multiple inheritance.
10.4.2.4
Native Type
In some cases (such as XML data and GUI elements), a JavaGram value is simply a
reference to the underlying Java implementation of the value. JavaGram provides the
native type for such values. These values are type-checked at runtime.
10.4.2.5
Vague Type
The JavaGram type vague encompasses all types. It is used when a variable can take
values belonging to different types. For example, the method sys.round() takes a vague
argument, because it accepts both integers and reals. When a value whose type is vague is
assigned to a non-vague variable, an explicit type cast is required.
10.4.2.6
Void Type
The type void is not really a type, but a pseudo type, and signifies lack of a value. It’s
used as the return type of a method that returns nothing. You can’t declare variables of
this type.
10.4.2.7
Implicit Type Cast
Where meaningful, JavaGram performs an implicit type cast to match the type of a value
to the type of the variable (or method parameter) it’s being assigned to. For example, if
you assign an integer value to a real variable, the integer is implicitly cast to real before
the assignment is performed. JavaGram rules for implicit type casting are as summarized
below.
From Type
int
int
Core Reference
To Type
char
real
347
char
char
symbol
date
int
real
string
int
native
vague
object
Any type
Any type
Any class
Any class
Any of its base classes
10.4.2.8
Explicit Type Cast
If the type of a value you’re assigning to a variable mismatches the variable’s type, you
can explicitly cast the value to the desired type before the assignment is performed. For
example,
int average = (sum / 2.7)@int;
converts the result of the division to int before assigning it to average. A type cast is
valid if the value can actually be converted to the specified type. Otherwise, it’s reported
as an error.
Incidentally, the @= operator provides a more succinct notation for expressing the above
example:
int average @= sum / 2.7;
This operator casts its right-hand side to the type required by its left-hand side and then
performs the assignment.
As a rule, a user-defined class can always be cast to a string, to produce the class’s
qualified name. For example, given a class Customer having the domain crm:
Customer c;
Customer@string
c.class@String
// gives: "crm\Customer"
// gives: "crm\Customer"
10.5 Literals
Literals are constant values that are established at load time. JavaGram allows you to
define literals for atomic types (except for stream), container types, and even user-defined
types.
10.5.1 Atomic Literals
10.5.1.1
Boolean Literals
There are only two boolean literals: true and false.
348
JavaGram Agile Development
10.5.1.2
Character Literals
A character literal is a single character enclosed in single quotes (e.g., 'a'). Commonly
used invisible characters as well as meta characters can be specified using the backslash
escape character:

'\n' (new line)

'\r' (carriage return)

'\t' (horizontal tab)

'\b' (backspace)

'\f' (form feed)

'\'' (single quote)

'\\' (backslash)
Additionally, a character can be specified using its octal code, which may be up to three
digits long. For example, '\012' represents the new line character, and '\0' represents the
null character.
Unicode character literals can be formed using the notation ‘\uxxxx’, where xxxx is a fourdigit hexadecimal value that specifies the Unicode character value. For example,
'\u00A9' represents the copyright © character.
Escaping any other character using a backslash has no effect.
10.5.1.3
Integer Literals
An integer literal consists of one or more digits, and may be signed by placing a + or –
before the number. Integers can be specified in one of three notations:

Decimal (base 10) notation, where the digits may range from 0-9. For example: 194.

Octal (base 8) notation, where the digits may range from 0-7. An octal integer must
begin with the digit 0. For example, 020 is equivalent to the decimal value 16.

Hexadecimal (base 16) notation, where the digits may range from 0-9 and A-F or a-f.
A stands for 10, B for 11, …, and F for 15. A hexadecimal integer must begin with 0x
or 0X. For example, 0xF1 is equivalent to the decimal value 241.
Small integer literals in the range -128 to 127 are optimized – they are stored once.
Therefore, for example, two separate occurrences of 20 in a program are not only equal
but also identical (i.e., refer to the same memory location).
10.5.1.4
Real Literals
A real literal may be specified in one of two notations:
Core Reference
349

Decimal point notation, for example, 3.14. Either side of the decimal point may be
empty. So, for example, .14 is the same as 0.14 and 3. is equivalent to 3.0.

Scientific notation, for example, 314e-2 or 314E-2. This is interpreted as 314.0 times
10 to the power of -2, which is the same as 314.0 divided by 100.
Reals may be signed in the same manner as integers by placing a + or – before them.
10.5.1.5
String Literals
A string literal is zero or more characters enclosed in double quotes (e.g., "hello"). The
characters inside a string literal may be escaped according to the same rules as a character
literal. Additionally, the double quote character itself may be specified as \". A single
quote character inside a string literal requires no escaping.
10.5.1.6
Symbol Literals
A symbol literal is similar to an identifier, except that it’s always preceded by a $
character (e.g., $name). Symbols that have the same name are internally stored once. This
makes symbol comparison easy and efficient. Symbols are especially useful as keys in
maps.
Unlike identifiers, you can define symbols that have non-alphanumeric characters in
them. You can do so by enclosing the name in braces. For example, a symbol having the
name task@work can be written as ${task@work}.
10.5.1.7
Date Literals
Date literals are used to represent calendar dates, and must appear inside [# and ]. The
order of the date components must always be: year, month, day of the month, all
expressed as numbers. The parts must be separated using the – or / character. For
example:
[#2007-12-25]
[#2007/12/25]
// 25th of December 2007
// 25th of December 2007
A date may optionally contain a time part, expressed in hours:minutes:seconds format.
For example:
[#2007-12-25 14:35:40]
For additional precision (e.g., as in timestamps) the time part may be optionally followed
by a nanosecond figure. For example:
[#2007-12-25 14:35:40.550]
An alternative convention is to express date/time as milliseconds since the UNIX epoch
time (number of milliseconds elapsed since midnight of 1st of January 1970). For
example:
350
JavaGram Agile Development
[#10000]
[#10000.50]
// is equivalent to [#1970-01-01 00:00:10]
// is equivalent to [#1970-01-01 00:00:10.50]
10.5.1.8
Binary Literals
A binary literal is the hexadecimal representation of a binary value. Example:
[##97A290C89282F87227EB20C2]
You don’t generally write a binary literal by hand, but use a method that converts a
binary value to this format. The sys.readBin() method, for example, takes a file name as
argument and returns the binary representation of its content.
JavaGram doesn’t provide a binary type. All binary literals have the type vague and are
type checked at runtime.
10.5.1.9
XML Literals
An XML literal is of type native and is always enclosed within <# and #>. For example,
native xml = <#<person id="1" name="John Smith"><job>Manager</job></person>#>;
sys.ppln(xml);
produces the following output:
<#
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<person id="1" name="John Smith">
<job>Manager</job>
</person>
#>
10.5.2 Composite Literals
10.5.2.1
Vector Literals
A vector literal is an ordered sequence of other literals, known as its elements. The
elements must appear inside [ and ] and be separated by commas. For example,
[2, 3, 5, 7, 11]
is a vector of the first five prime numbers. The elements of a vector need not be of the
same type. For example, here is a vector of five elements that contains mixed types,
including another vector:
["hello", 10, 3.14, $there, [10, 20]]
The type of a vector literal is deduced from its elements. For example,
Core Reference
351
[10, 20, 30]
// has the type vector<int>
[10, 20.0, 30] // has the type vector, which is the same as vector<vague>
[[$one], [], [$two, $three]]
// has the type vector<vector<symbol>>
An empty vector literal is specified as [].
10.5.2.2
List Literals
A list literal is an ordered sequence of other literals, known as its elements. The elements
must appear inside $( and ) and be separated by commas. For example,
$(1, 2.0, "three")
is a list of three elements.
Every list has a head (its first element) and a tail (the rest of the list). The head of the
above list is 1 and its tail is $(2.0, "three"). The tail of a list that has only one element is
null (which also represents an empty list).
When a list literal appears inside another literal (i.e., a vector, list, map, or object), you
can start the list with ( instead of $(. For example, in the map
[$company=>"Acme”, $locations=>("Chicago", "London", "Sydney")]
$locations is specified as a list of three cities.
10.5.2.3
Map Literals
A map literal associates a set of unique keys with a set of values. All map keys must be
atomic literals, whereas the values can be any literals. Map key/value pairs must appear
inside [ and ] and be separated by commas. For example,
[$name => "Bill", $dob => [#1977-08-20], $sex => "male"]
is a map, where $name, $dob and $sex are the keys. If a key appears more than once in a
map, the last instance overrides the earlier ones.
The type of a map literal is deduced from its elements. For example,
[$name => "Bill", $sex => "male"]
[$name => "Bill", $age => 40]
[$parts => [20, 25, 70]]
// has the type map<symbol, string>
// has the type map<symbol, vague>
// has the type map<symbol, vector<int>>
An empty map is specified as [=>].
352
JavaGram Agile Development
10.5.2.4
Object Literals
Ordinarily, an object is created at runtime using the new operator. An object literal is an
instance of a class created at load time rather than runtime. It provides a convenient
means of pre-creating objects.
For convenience, when you try to print an object (using, for example, sys.print() or
sys.pp()) you will get a literal representation of it. This has many advantages:

It makes it very easy to see the structure of an object and the value of its individual
fields.

It can be used as a serialized representation of the object, making it ready for storage
in files or databases, or transmission over a network.

It makes the object protocol independent, so that it can be reconstructed on a different
machine reliably.
In fact, JavaGram completely hides the binary representation of all objects.
Object literals must conform to this notation:
[@ClassName …field mapping…]
For example, given the class
class Person {
string name;
date dob;
symbol sex;
// methods...
}
The following object literals are all valid:
[@Person name => "Alex", dob => [#1972-10-23], sex => $male]
[@Person name => "Linda", sex => $female]
All unspecified fields (e.g., dob in the second example) are assigned the null value.
Object literals can be nested to any depth.
Because object literals bypass the normal mechanism of creating an object (i.e., class
constructors), they can violate any rules imposed by constructors and get away with it.
This can be both advantageous and dangerous, so use it wisely.
Core Reference
353
10.6 Expressions
An expression is a computation that produces a value. Examples include: literals, use of
various operators, method calls, references to objects fields, and type casting. Expressions
are formed in a hierarchical manner – simple expressions are combined to produce more
complex ones, and so on.
In discussing expressions, we sometimes refer to an lvalue. This is something that can be
used on the left-hand side of an assignment (i.e., something that can be assigned to).
Examples include: a variable, an object field reference, a vector element reference, a map
element reference.
10.6.1 Unary Operators
10.6.1.1
+ Operator
The unary + operator is redundant, but is included for completeness. It may appear before
any numeric expression and has no effect on the expression – JavaGram simply ignores
it. Examples:
+10
+sum
10.6.1.2
– Operator
The unary - operator negates a numeric expression. The operand must be of type char,
int, or real. For example:
-'A'
-x
-(x + y)
// gives -65, where 65 is the ASCII code of 'A'
// assuming that x is 10.2, gives -10.2
// assuming that y is 5, gives -15.2
10.6.1.3
++ Operator
The auto increment operator expects a char or int lvalue operand, and increments the
operand by one. The operator may appear before the operand (prefix) or after it (postfix).
In its prefix form, the operand is incremented and then returned. In its postfix form, the
operand is returned and then incremented. Examples:
++10
++n
n++
++c
//
//
//
//
invalid (not an
assuming that n
assuming that n
assuming that c
lvalue)
is 5, n becomes 6 and it returns 6
is 6, n becomes 7 and it returns 6
is 'A', returns 'B'
10.6.1.4
-- Operator
The auto decrement operator is like the auto increment operator, but decrements the
operand by one. Examples:
--10
--n
354
// invalid (not an lvalue)
// assuming that n is 5, n becomes 4 and it returns 4
JavaGram Agile Development
n--
// assuming that n is 4, n becomes 3 and it returns 4
10.6.1.5
! Operator
The logical negate operator expects a boolean operand and reverses its value. Examples:
!correct
// assuming that correct is true, gives false
!(10 > 20) // gives true
!10
// invalid (not a boolean operand)
10.6.1.6
~ Operator
The bitwise negate operator toggles the bits in its integer operand. Because the left-most
bit of an integer is its sign bit, you’ll notice that bitwise negation of a positive integer will
produce a negative one. However, this is not significant because this operator is typically
used when an integer is treated as a sequence of bits rather than a numeric value.
Examples:
~0xFF
~x
~10.2
// gives -256
// assuming that x is 500, gives -501
// invalid (not an integer operand)
10.6.1.7
Typeof() Operator
The typeof() operator can be used to obtain a symbolic representation of an expression’s
type. Examples:
typeof(10 + 2.1)
typeof(10 + false)
typeof(new Exception(""))
// gives $real
// gives $string
// gives $Exception
This operator is useful when you want to store a type somewhere (e.g., use it as a key in a
map). To find out if an expression is of a certain type, use the instanceof operator instead
(described below).
Please note that when typeof is used on a class or class instance, it returns a symbol
which denotes the fully qualified class name. For example, if a class Person has the
domain crm\bo then:
typeof(new Person())
typeof(Person)
// gives ${crm\bo\Person}
// gives ${crm\bo\Person}
Incidentally, this is similar to using .symbol on the class itself:
Person.symbol
// gives ${crm\bo\Person}
10.6.1.8
Valueof() Operator
The valueof() operator is useful for ensuring that an expression returns a non-null value.
For example, assuming that foo() has a return type of int:
Core Reference
355
valueof(foo())
valueof(foo(), 10)
// gives the return value of foo(), or 0 if it returns null
// gives the return value of foo(), or 10 if it returns null
When present, the second operand acts an explicit default value. Otherwise, the default
value is implicit and determined from the type of the first operand, as summarized below.
First Operand Type
Implicit Default Value
int
real
string
char
boolean
Any other type
0
0.0
""
'\0'
false
null
10.6.1.9
Arg() Operator
The arg() operator can be used inside any method, according to two rules:

arg(null) and arg(-1) both return the number of arguments passed to the method.

arg(i) returns the value of an argument, where i denotes the zero-based position of
the argument.
This operator is useful for methods that take a variable number of arguments, and can be
used to retrieve the actual argument values.
10.6.2 Binary Operators
10.6.2.1
= and @= Operators
The = (assignment) operator is used for assigning the value of an expression (right
operand) to an lvalue (left operand). Examples:
r = 20 * 3 // assuming that r is of type real, assigns 60.0 to r
v[2] = $ok // stores $ok in the 3rd slot of vector v
m[$one] = 1 // stores 1 against the key $one in the map m
The right value is implicitly type cast to the lvalue’s type if necessary and permissible.
Furthermore, an instance (object) of a class x can be assigned to a variable that is of type
object, vague, or any of x’s super classes. No conversion is performed in such cases, as
it’s unnecessary.
Where an implicit type cast is insufficient, you must use explicit casting to make the
types compatible. For example:
string s = 2.1 @ string;
356
JavaGram Agile Development
Alternatively, you can use the @= operator. This operator casts its right operand to the type
of its left operand before performing the assignment. For example:
string s @= 2.1;
10.6.2.2
+ and += Operators
The + operator takes a wide range of operand types and its semantics is tied to the
operand types. Its most common use is for adding numeric values. If both operands are of
type int or char then the result will be an integer. If at least one of the operands is a real
then the result will be a real. Examples:
10 + 2
'A' + 2
2 + 2.3
// gives 12
// gives 67
// gives 4.3
Adding a number to a string, symbol, or boolean is treated as string concatenation. In
fact, any combinations of these can be concatenated, except for two booleans.
Concatenation of two symbols will result in a symbol rather than a string. Examples:
10 + false
true + "one"
false + $one
"one" + $two
$one + $two
true + false
//
//
//
//
//
//
gives "10false"
gives "trueone"
gives "false$one"
gives "one$two"
gives $onetwo (result is a symbol, not a string)
invalid (two booleans can’t be added)
An integer added to a date adds a number of days. Examples:
[#2010-04-16] + 39
// gives [#2010-05-25]
You can use the + operator to concatenate two lists, two vectors, or two maps. The
operands are not affected; a new container is created to contain the overall result.
Examples:
$(10) + $(20, 30)
// gives $(10, 20 , 30)
[$one] + [$two, $three]
// gives [$one, $two, $three]
[$one=>1] + [$one=>2.0, $two=>3] // gives [$one => 2.0, $two => 3]
The returned value’s type is deduced from the operands. For example, the concatenated
vector in the above example will have the type vector<symbol> and the concatenated map
will have the type map<symbol, vague>.
The += operator causes the two operands to be added first and the result assigned to the
left operand. In other words,
x += y
Core Reference
// is equivalent to x = x + y
357
except that the shorter version is optimized. For example, applying it to two vectors
causes the right vector to be appended to the end of the left vector (no intermediate vector
is produced). Similarly, applying it to two maps causes the right map to be added to the
left map without producing an intermediate map.
Of course, for += to be valid, the type of the left operand must be capable of receiving the
resulting value. Example:
n += 2.3
// invalid (assuming that n is of type int)
Finally, += can be used for adding a GUI element to a container (see Chapter 11).
10.6.2.3
– and –= Operators
The - operator is commonly used is for subtracting numeric values. If both operands are
of type int or char then the result will be an integer. If at least one of the operands is a
real then the result will be a real. Examples:
10 - 2
'A' - 2
2 - 2.3
// gives 8
// gives 63
// gives -0.3
An integer subtracted from a date subtracts a number of days. Subtracting two dates
yields the number of days between them, ignoring any specified time of day. Examples:
[#2011-01-01] - 7
[#2010-04-16 23:02:59] - [#2010-04-15]
// gives [#2010-12-25]
// gives 1
You can use the - operator on two vectors or two maps. The operands are not affected; a
new container is created to contain the elements of the first operand, excluding those in
the second operand. Examples:
[$one, $two, $one] - [$one]
[$one=>1, $two=>2.0] - [$one=>10, $three=>30]
// gives [$two]
// gives [$two=>2.0]
The returned value’s type is deduced from the operands.
The -= operator causes a subtraction followed by an assignment. In other words,
x -= y
// is equivalent to x = x - y
except that the shorter version is optimized. For example, applying it to two vectors
causes the right vector’s elements to be removed from the left vector (no intermediate
vector is produced).
Finally, -= can be used for removing a GUI element from a container (see Chapter 11).
358
JavaGram Agile Development
10.6.2.4
* and *= Operators
The * operator is used for multiplying numeric values (integers, characters, reals). When
both operands are characters or integers, the result will be an integer. Otherwise, it will be
a real. Examples:
2 * 10
2 * 'a'
2 * 5.5
// gives 20
// gives 130
// gives 11.0
The *= operator causes a multiplication followed by an assignment:
x *= y
// is equivalent to x = x * y
10.6.2.5
/ and /= Operators
The / operator is used for dividing numeric values (integers, characters, reals). When
both operands are characters or integers, the result will be an integer. Otherwise, it will be
a real. Examples:
10 / 3
'a' / 2
10.0 / 4
// gives 3
// gives 32
// gives 2.5
Division by zero is considered illegal and will result in an exception.
The /= operator causes a division followed by an assignment:
x /= y
// is equivalent to x = x / y
10.6.2.6
% and %= Operators
The % operator is used for getting the remainder of dividing integers or characters.
Examples:
10 % 3
10 % 4
'a' % 2
// gives 1
// gives 2
// gives 1
Division by zero is considered illegal and will result in an exception.
The %= operator causes a remainder followed by an assignment:
x %= y
// is equivalent to x = x % y
10.6.2.7
Equality (==, ?=, !=, ===, !==) Operators
These operators allow you to compare two values for equality (==), guarded equality (?=),
inequality (!=), identicity (===), or non-identicity (!==). Unlike Java (which requires the
type of the operands to match), you can use these operators on any values. Examples:
Core Reference
359
'a' == 64
X ?= 10
10 != "ten"
10 === 10
10 === 10.0
"man" == "man"
"man" === "man"
$man === $man
//
//
//
//
//
//
//
//
gives true
is equivalent to: x == null || x == 10
gives true
gives true (because 10 is in the range -128 to 127)
gives false
gives true
gives false (have different addresses)
gives true (symbols are stored only once)
The basic rule to remember is that identicity means that two values have exactly the same
memory location.
10.6.2.8
Relational (<, <=, >, >=) Operators
These operators work on a variety of operand types and can be used to compare:

Two numeric values (integers, characters, reals)

Two strings

Two symbols

Two dates
For strings and symbols, the comparison is lexicographic and case sensitive. Examples:
10 <= 10.2
'a' > 100
"apple" < "pear"
$apple < $pear
[#2007-1-1] > [#2006-12-30]
//
//
//
//
//
gives
gives
gives
gives
gives
true
false
true
true
true
10.6.2.9
Logical (&&, ||) Operators
The && (logical and) and || (logical or) operators take two boolean operands and return a
boolean result. && returns true if and only if both operands evaluate to true. || returns
true if at least one of the operands evaluates to true. Examples:
n > 0 && n < 10
n == 5 || n < 0
n > 5 && n
// gives true when n is 5, and false when it’s 10
// gives true when n is -2, and false when it’s 1
// invalid (both operands must be boolean)
10.6.2.10 Bitwise (&, &=, |, |=, ^, ^=) Operators
The & (bitwise and), | (bitwise or), and ^ (bitwise xor) operators take two integer
operands and return an integer result whose individuals bits are set according to these
rules:

& sets a bit to 1 if both corresponding bits are 1.

| sets a bit to 1 if either of the corresponding bits is 1.

^ sets a bit to 1 if one of the corresponding bits is 1, but not both.
360
JavaGram Agile Development
Examples:
0x70 & 0x10
0x70 | 0x10
0x70 ^ 0x10
// gives 16
// gives 112
// gives 96
The assignment versions of these operators have the usual meaning:
x &= y
x |= y
x ^= y
// is equivalent to x = x & y
// is equivalent to x = x | y
// is equivalent to x = x ^ y
10.6.2.11 Bitwise Shift (<<, <<=, >>, >>=) Operators
The << (bitwise left shift) operator takes two integers operands and returns an integer
that’s the result of left-shifting the bits in the left operand by the number of positions
denoted by the right operand. The >> (bitwise right shift) operator does the opposite.
Examples:
2 << 3
100 >> 2
// gives 16
// gives 25
The assignment versions of these operators have the usual meaning:
x <<= y
x >>= y
// is equivalent to x = x << y
// is equivalent to x = x >> y
10.6.2.12 List/vector/map access Operator
The square brackets [] operator is used for accessing the contents of lists, vectors, and
maps. Examples:
digits[0] = "zero"; // Update the first element of digits vector
age[$jack] = 21;
// Store age of Jack in age map
With lists and vectors, the operator expects that the element exists – using an out of range
index will result in an error. With maps, the key need not be in the map, in which case,
null is returned.
10.6.2.13 Object access (. and ?.) Operators
The dot (.) operator is used for accessing the methods and fields of an object or class, as
well as the properties of a GUI element.
prod.price = 500;
prod.addToCatalog();
int n = Product.MAX_ID;
table.autoSize = true;
Core Reference
//
//
//
//
Update an object's field
Invoke an object's method
Access a static field of a class
Set a GUI element's property
361
You can also use the dot operator to look up a map, provided the key is a symbol. This is
equivalent to the use of the [] operator. Example:
price.$bread = 3.15;
// Store data in price map
real cost = quantity * price.$bread; // Look up price map
The guarded dot (?.) operator can be used for accessing object methods and fields or map
elements without having to check that the object or map is non-null. For example,
if (prod != null)
prod.addToCatalog();
can be more elegantly written as
prod?.addToCatalog();
If prod evaluates to null then the method is not called (if the method has a return type,
null is returned). If you use this operator to access a field and the object happens to be
null, you’ll get null.
You can also use this notation in an lvalue. The effect when the object or map is null is
that the assignment is not performed, but the right-hand value is still returned. Example:
// 500 is added to cost even when prod is null:
cost += prod?.price = 500;
// prod as an object
cost += prod?.$price = 500;
// prod as a map
The dot operator can also be used directly on a class name to access its static members. In
this case, the following may also be used after the dot:

symbol (e.g., Customer.symbol) gives the class name as a symbol (e.g., $Customer).

singleton (e.g., CRM.singleton) gives the single instance of the class, provided the
class is defined as a singleton class.
Finally, the syntax obj.class gives the class of obj. For example, if p is of type Product
then p.class@string gives "Product".
10.6.2.14 @ Operator
The cast operator @ is used to convert a value from one type to another, provided the
conversion is permissible. Examples:
10 @ real
10.0 @ int
10 @ string
"10" @ int
sys.date() @ int
Customer@string
362
//
//
//
//
//
//
gives 10.0
gives 10
gives "10"
invalid: no conversion from string to int
converts a date to its numeric timestamp
gives "Customer"
JavaGram Agile Development
A common use of @ is for converting an object to a user-defined type:
object obj;
//...
Customer cust = obj @ Customer;
This conversion is checked for validity at runtime because the actual value of obj may not
be known in advance.
You can also use @ after a pseudo class method name. Example:
map m = sql.getRow @ map(rs);
In this case, the cast instructs sql.getRow() to return a map.
Finally, you can cast an expression of type native or vague to a GUI element for the
purpose of accessing its properties. Example:
panel@<Panel>.title = "Personal Details";
10.6.2.15 Instanceof Operator
The instanceof operator is used to check if a value is of a given type. Examples:
10 instanceof int
// gives true
obj instanceof Customer // gives true if obj is a Customer class instance
10.6.3 Ternary Operators
10.6.3.1
Conditional Expressions
A conditional expression has three parts: a boolean condition, a ‘then’ expression and an
‘else’ expression. If the condition evaluates to true, the ‘then’ expression is evaluated and
returned. Otherwise, the ‘else’ expression is evaluated and returned. Example:
n < 0 ? $negative : $positive
// returns $positive when n is zero
10.6.3.2
Asynchronous Method Invocation
An asynchronous method invocation allows you to call a method such that you don’t
have to wait for it to complete. Execution proceeds to the next statement as soon as the
call is made. When the call eventually completes, a callback is invoked to complete the
processing. When the async method throws an exception:

If an error callback is also specified, it will be invoked instead.

If no error callback is specified, the main callback is still invoked and will be
responsible for exception handling.
Core Reference
363
In either case, you can use sys.getAsyncErr() to get hold of the exception.
An asynchronous method invocation may take one of the following three forms:



MethodCall -> Callback -> ErrCallback
lvalue = MethodCall -> Callback -> ErrCallback
Type var = MethodCall -> Callback -> ErrCallback
where ErrCallback is optional. A callback can be any valid expression or statement, or
even a block.
Example:
vector result = crm.search(criteria) -> viewer.show(result)
-> viewer.error("search failed");
For the purpose of the error callback, you can use the sys.getAsyncErr() method to
retrieve the exception.
JavaGram executes each asynchronous method call in a separate thread. If the call is to a
remote method then a separate thread on the client-side handles the processing of all
callbacks. All this is handled transparently to the programmer.
You can also use a guarded version of the asynchronous call syntax to code for both
synchronous and asynchronous clients. For example:
User u = login() ?? sys.async -> setUser(u) -> failed(sys.getAsyncErr());
or
User u = login() ?> sys.async -> setUser(u) -> failed(sys.getAsyncErr());
The condition appearing after ?? or ?> must be a boolean expression. When this condition
evaluates to true, the call is performed as before. When this condition evaluates to false,
login() is called synchronously and no callback is invoked, unless ?> is used, in which
case the callback(s) are still performed but synchronously.
Because Flash is single-threaded, the following restrictions apply to a Flash client:

All remote calls in a Flash client must be asynchronous because Flash cannot block
on a call (calling a remote method synchronously will result in a runtime error).

Non-remote asynchronous calls are always handled synchronously (due to lack of
multi-threading).

When login() is a remote call, it is always called asynchronously and the callback(s)
are used, regardless of whether the guard condition evaluates to true or false.
364
JavaGram Agile Development
10.6.4 N-ary Operators
10.6.4.1
Vector Operator
The vector operator (same name as the type) allows you to dynamically create a vector. It
takes zero or more operands, each of which may be an arbitrary expression, and returns a
vector each of whose elements is the result of evaluating the expressions. For example,
vector vec = vector(10, 100 + "dollars", 100 / 2.4);
sets vec to [10, "100dollars", 41.66666666666667].
The type of the resulting vector is deduced from the operand types. For example, if all
elements are integers then the vector will have the type vector<int>.
The distinction between a vector literal and a vector created using the vector operator is
very significant. For example, the above vector is created each time the line of code is
executed, whereas the vector literal [1, 2, 3] is created only once at load time.
10.6.4.2
List Operator
The list operator (same name as the type) allows you to dynamically create a list. It
takes zero or more operands, each of which may be an arbitrary expression, and returns a
list each of whose elements is the result of evaluating the expressions. For example,
list lst = list(10, 100 + "dollars", 100 / 2.4);
sets vec to $(10, "100dollars", 41.66666666666667).
The distinction between a list literal and a list created using the list operator is very
significant. For example, the above list is created each time the line of code is executed,
whereas the list literal $(1, 2, 3) is created only once at load time.
10.6.4.3
Map Operator
The map operator (same name as the type) allows you to dynamically create a map. It
takes zero or more pairs of operands, each of which may be an arbitrary expression, and
returns a map each of whose key/value pairs is the result of evaluating the expressions.
For example,
map m = map(1 => "one", 2 + 3 => "two");
sets m to [1 => "one", 5 => "two"].
The type of the resulting map is deduced from the operand types. For example, the above
map will have the type map<int, string>,
Core Reference
365
The distinction between a map literal and a map created using the map operator is very
significant. For example, the above map is created each time the line of code is executed,
whereas the map literal [1 => "one", 2 => "two"] is created only once at load time.
As with map literals, all key expressions must evaluate to atomic values.
10.6.4.4
New Operator
The new operator is used to create an instance of a class. Its operands must follow the
same syntax as a method call for a class constructor. If the class has no constructor then a
call to an implicit constructor with no argument must be made. For example,
Customer c = new Customer("John", 101);
creates an instance of the Customer class and invokes its constructor that takes a string and
an integer as arguments, whereas
Record r = new Record();
creates an instance of the class Record and invokes its parameter-less constructor if it has
one.
The rules for invoking constructors are specified in the sections on classes (see below).
Like Java, JavaGram allows you to anonymously subclass a class when using the new
operator. This is typically used to override one or more class methods purely for the
benefit of the created object. Example:
Customer c = new Customer("John", 101) {
// Override the Customer.format() method:
public string format () {
//...
}
};
10.6.4.5
Object Operator
The object operator provides a more direct way of creating object instances. Like object
literals, this method bypasses class constructors and allows you to specify the values for
class fields directly. For example, let’s say you have a class called Person which has a
name and a dob (date of birth) field. You can create an instance of Person like this:
Person p = object(Person, name=>"John", dob=>[1982-10-22]);
Like vectors and map, the difference between an object created this way (or using new)
and an object literal is significant. The latter is created once at load time, whereas the
former is created each time the code is executed.
366
JavaGram Agile Development
10.6.5 Operator Precedence
When multiple operators are used in the same expression, it’s important to be sure of the
order in which the operators are evaluated. The following table summarizes the order.
The higher an operator in the table, the higher is its precedence (i.e., it’s performed first).
For those operators in the same row (i.e., having the same precedence) evaluation is
always from left to right.
Operator Precedence (high to low)
@, ??, ?>, ->, typeof, arg, vector, list, map, object, new
[]
++, --, unary +, unary -, ~, !
*, /, %
binary +, binary <<, >>
<, <=, >, >=, instanceof
==, !=, ===, !===
&
^
|
&&
||
? :
=, @=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
()
You can always use parentheses to override the precedence of operators. For example, in
x + y * z if the intent is to add x and y and then multiply by z then this should be written
as (x + y) * z.
10.6.6 Delayed Strings
A common programming pattern is to concatenate strings with other values (such as
numbers) to produce formatted strings, especially for output purposes. For example,
sys.println("found " + n + " records in " + s + " seconds");
would produce something like the following output:
found 2000 records in 0.05 seconds
This line of code can be more elegantly written using a delayed string:
sys.println($"found {n} records in {s} seconds");
The $ preceding the string indicates that it’s a delayed string. Unlike normal strings which
are created at load time, a delayed string is created at runtime. Any occurrence of {} in a
Core Reference
367
delayed string must contain a valid expression. The expression can be as complex as you
like, but must not be empty. When the delayed string is evaluated, these expressions are
evaluated from left to right and then spliced into the string to produce the final value.
10.6.7 Method Invocation
A call to a method that has a non-void return type is a valid expression. Example:
int n = sys.min(x, y);
Whether a method can be called or not, depends on its visibility and the scope from
which it’s called. These considerations are discussed in the sections on classes (see
below).
10.7 Statements
A statement is a structured piece of computation. Examples include: if statement, loops,
and throwing an exception. Like expressions, statements are formed in a hierarchical
manner by combining simpler statements to form more complex ones.
Whereas the aim of an expression is to produce a value, the purpose of a statement is to
cause a side-effect, such as changing the value of a variable, producing output, or
modifying an object.
The simplest statement is a semicolon on its own, denoting an empty statement. This can
be useful in some situations, such as writing a loop that has an empty body.
10.7.1 Expression Statement
Any expression followed by a semicolon is a valid statement. Examples:
10;
x + 10;
++x;
// useless (no side-effect)
// useless (no side-effect)
// useful (increments x by 1)
10.7.2 Blocks
A block allows you to group multiple statements such that the whole thing can be treated
as a single statement. Additionally, a block introduces a new scope, allowing you to
define local variables that override variables defined in parent scopes. All the statements
in a block must be enclosed by a pair of braces. Blocks can be nested to any depth.
Example:
{
int n = 10;
{ int n = 20;
sys.println(n);
}
sys.println(n);
}
368
JavaGram Agile Development
This will produce the following output:
20
10
It’s worth noting that JavaGram differs from Java in this respect. Java will not allow the
re-declaration of the variable n in the inner scope.
10.7.3 If Statement
The if statement is used for writing conditional logic. Example:
if (n >= 0)
sys.println("n is positive");
The condition must always appear in a pair of parentheses and evaluate to a boolean
value. If the condition evaluates to true then the statement following the condition is
performed. An optional else part can be included to define a statement to be performed
when the condition evaluates to false. Example:
if (n >= 0)
sys.println("n is positive");
else
sys.println("n is negative");
More complex cases can be written by combining multiple if statements. Example:
if (n == 0)
sys.println("n is zero");
else if (n > 0)
sys.println("n is positive");
else
sys.println("n is negative");
10.7.4 While Loop
The while loop is used to repeatedly execute a statement (loop body) while a condition
holds true. Example:
int i = 0, sum = 0;
while (i < 10)
sum += i++;
The condition following while must appear in parentheses and evaluate to a boolean
value. The statement following the condition is executed and the process is repeated until
the condition evaluates to false.
Core Reference
369
10.7.5 Do Loop
The do loop is used to repeatedly execute a statement while a condition holds true.
Example:
int i = 0, sum = 0;
do
sum += i++;
while (i < 10);
The key difference between a while loop and a do loop is that in the former the condition
is evaluated before the loop body is executed, whereas in the latter the loop body is
executed before the condition is evaluated. As a result, the body of a do loop is always
executed at least once.
10.7.6 For Loop
The for loop is typically used to iterate a loop counter through successive values.
Example:
int sum = 0;
for (int i = 0; i < 10; ++i)
sum += i;
A pair of parentheses must appear after for and contain three things: initializer, loop
condition, and incrementor. These must be separated by semicolons. Any of these can be
empty. Therefore, a minimal for loop will look like this:
for (;;) {
// loop body
}
Because there is no condition in this loop, it will iterate indefinitely, unless a break or
return inside the loop body terminates it.
for loops are very flexible. You can declare local loop variables (e.g., i in the above
example), have multiple loop counters, and multiple parts in the incrementor. If you use a
local loop variable, its scope is limited to the loop. Therefore, i is not defined outside the
above loop. Another example:
int sum = 0;
vector<int> v = [10, 29, 20, 100];
for (int i = 0, n = sys.length(v); i < n; ++i)
sum += v[i];
Although loop variables such as i and n need not be defined locally (they can be defined
before the loop), in most cases locally defined variables are preferred.
370
JavaGram Agile Development
10.7.7 For-in Loop
A for-in loop is used for iterating through the elements of a collection. Although you can
do the same using other loop types, a for-in loop provides a more succinct notation for
this purpose. Example:
int sum = 0;
vector<int> v = [10, 29, 20, 100];
for (int n in v)
sum += n;
The loop variable (e.g., n above) must be local to the loop, and the expression appearing
after in must evaluate to a vector, map, or GUI container. Each time round the loop, n is
set to the next value in the vector v.
If the container is a map then the loop variable iterates through the map keys (not values).
Example:
real sum = 0;
map<symbol, real> price = [$apple=>3.5, $orange=> 2.4, $pear=>1.9];
for (symbol fruit in price)
sum += price[fruit];
10.7.8 Break Statement
The break statement is used to terminate the flow of control in a loop or in a switch
statement, causing the flow to be transferred to after the loop or switch. Example:
while (true) {
string s = sys.strTrim(sys.readln());
if (s == "quit")
break;
sys.println(s);
}
10.7.9 Continue Statement
The continue statement is used in a loop to transfer the flow of control to the loop
condition. Example:
while (true) {
string s = sys.strTrim(sys.readln());
if (sys.length(s) == 0)
continue;
if (s == "quit")
break;
sys.println(s);
}
Core Reference
371
10.7.10
Return Statement
The return statement is used in a method to transfer flow of control to the method caller.
If the method has a non-void return type then the return statement must return a value of
the same type. Example:
public static real average (vector v) {
real sum = 0;
int count = 0;
for (vague n in v) {
if (n instanceof int || n instanceof real) {
sum += n @ real;
++count;
}
}
return sum / count;
}
10.7.11
Switch Statement
The switch statement is used to select amongst a number of possible execution paths
based on the value of an expression. Example:
switch (cmd) {
case $send:
send(data);
break;
case $receive:
receive(buf);
break;
case $ack:
acknowledge();
break;
default:
sys.println($"invalid command {cmd}");
break;
}
The expression immediately appearing after switch is called the selector and must be in
parentheses and must evaluate to an integer, character, or symbol. The switch may
contain zero or more cases, each starting with the keyword case, followed by a literal (or
static field) that has the same type as the selector. An optional default clause will act as
a catch-all. The selector value is compared against each case and if a match is found, the
statements following the case are executed. The last statement of each case is usually a
break (or return). Otherwise, execution will continue to the next case, and so on. If no
match is found and a default clause is specified, it’s exercised.
372
JavaGram Agile Development
10.7.12
Throw Statement
The throw statement is used to raise an exception; the latter must be an instance of the
predefined Exception class or a subclass of it. Example:
class SearchException extends Exception {}
//...
throw new SearchException();
10.7.13
Try Statement
The try statement is used for catching and handling potential exceptions. Example:
real sum = 0;
vector v = [1, "two", 3.0];
try {
for (vague n in v) {
if (n instanceof int || n instanceof real)
sum += n @ real;
else
throw new Exception($"number expected: {n}");
}
sys.println(sum);
}
catch (Exception e) {
sys.println($"Error: {e.getMessage()}");
}
Note that the try block will catch exceptions thrown either directly inside the block (as in
the above example) or indirectly by any of the methods called in the block.
One or more catch clauses must appear after the try block. Multiple catch blocks allow
you to catch different types of exceptions. The order of catch clauses is significant –
when an exception is raised in the try block, the exception type is compared against the
exception types in the catch clauses in the order they appear. As soon as a match is
found, that clause is exercised. Therefore, you should list your catch clauses in the order
of specific to generic. If you have a catch clause for catching Exception, it should appear
last, because it’s the most generic and will catch anything.
A try statement may be optionally followed by a finally block. This block is always
executed after the try block, regardless or whether any exception is thrown or not. It’s
also permissible to have a try block with a finally block and no intermediate catch
blocks. Example:
stream s = null;
try {
s = sys.open(filePath, "r");
string str;
while ((str = sys.readln(s)) != null)
Core Reference
373
sys.print(str);
sys.flush();
}
finally {
sys?.close(s);
}
10.7.14
Synchronized Statement
The synchronized statement is used to ensure that only one thread at a time can execute a
section of code. Example:
synchronized ($mutex) {
//...
}
The keyword synchronized must be followed by a pair of parentheses that contain an
expression. This can be any expression of any type, even a literal, and is used as the lock
object. However, you should be careful about choosing something appropriate, so that it
remains the same for multiple threads attempting to execute the synchronized code
section. In the above example, we’ve used a symbol (which is guaranteed to be a
singleton). You may also use class field members created specifically for this purpose.
When a thread attempts to execute a synchronized section, it first attempts to obtain a
lock on the specified object. After exiting the block, the thread will release the lock. The
lock ensures that any other thread attempting to execute the section will block until the
locking thread has released the lock.
10.7.15
Query Statement
The query statement is used to allocate a database connection to the thread as a section of
code is executed. Example:
query ($ordersDb) {
//...
}
The keyword query must be followed by a pair of parentheses that contain an expression.
This expression can be an explicitly created connection but is more generally a reference
to a server configuration file entry detailing database connection parameters. Example:
$database => [
$ordersDb => [
$name => "Orders",
$url => " jdbc:oracle:thin:@uklondon02:1521:ORDERS",
$user => "batch", $password => "z$iH;V",
$driver => " oracle.jdbc.OracleDriver"
]
]
374
JavaGram Agile Development
Where a database configuration specifies database connection pooling, the query block
allocates a connection from the pool and de-allocation returns the connection to the pool,
making it available to other threads. Where pooling is not specified, allocation and deallocation imply opening and closing the connection.
After exiting the block, any statements or result sets that have been created within the
block and left open are closed. Finally, the database connection is de-allocated.
10.7.16
Transaction Statement
The transaction statement behaves like the query statement but, in addition, groups a
series of SQL statements into a single transaction. Example:
transaction ($ordersDb) {
//...
}
As control flow causes the thread to leave the block, the underlying database transaction
is committed. Should an exception be thrown within the block, a rollback is performed
and the pending database changes are not committed.
Should any of the enclosed SQL statements or procedures they call issue a commit,
rollback or other transaction control statements, the behavior is undefined.
10.7.17
Assert Statement
The assert statement is used for documenting and verifying invariants. An invariant is a
boolean condition that must hold true. For example, a method that inserts a value into a
collection expects the collection not to be null:
void insertProduct (vector<Product> products, Product product) {
assert products != null;
//...
}
During execution, the assert condition is evaluated and if the result is false, an error is
raised.
10.8 Classes
The syntax and semantics for JavaGram class definition and access closely parallel those
of Java, thus enabling Java programmers to become productive in JavaGram very
quickly.
Key differences with Java are:

A class name need not match the file name in which it appears. Multiple class
definitions may appear in the same file.
Core Reference
375

JavaGram classes may not be nested.

JavaGram supports multiple inheritance through the use of mutual classes.

There is no notion of an interface in JavaGram. Abstract classes are used to represent
the same.

JavaGram classes may contain local and remote methods. Remote calls are managed
transparently.

JavaGram supports the notion of local and remote classes. Remote class objects are
managed through proxy objects in a manner that’s transparent to the programmer.

In addition to field and method members, you can define text and GUI members for a
JavaGram class. A text member behaves like a method and allows you to do advanced
text processing. A GUI member behaves like a field and allows you to define user
interface components declaratively and hierarchically.
A class itself can be treated as a value, and therefore assigned to variables or passed to
methods. You must use the type vague for this purpose. Example:
vague klass = Person;
// is valid provided Person is a defined class
10.8.1 Class qualifiers
You can specify the following qualifiers by placing the keyword before a class definition:

An abstract class is one that can’t be instantiated, usually because it has abstract
methods whose actual definitions are deferred to subclasses. However, an abstract
class does not have to contain abstract methods.

A final class is one that can’t be subclassed, for example, as a security measure.

A clocal class is one that’s intended to run on the client side. JavaGram prevents such
classes from being executed on the server side. This qualifier has no effect in
standalone deployment.

An slocal class is one that’s intended to run on the server side. JavaGram prevents
such classes from being executed on the client side. This qualifier has no effect in
standalone deployment.

A remote class is one whose objects will always reside on the server side, but can be
accessed from the client side. Such access is through proxy objects that are managed
by JavaGram. This qualifier has no effect in standalone deployment.

A singleton class is one that may have a maximum of one instance.

A mutual class is designed with the intention of use for multiple inheritance. By
definition, all the super classes of a mutual class must also be mutual. You can define
a class that has multiple direct super classes, provided at most one of them is nonmutual.

A generated class is one that has been generated by a Wizard.
376
JavaGram Agile Development
Unlike Java, you don’t specify any visibility for a class – all classes are assumed to be
public.
10.8.2 Class Properties
Class properties are read-only and can be accessed using the Class.Property notation. The
following properties are defined:

The symbol property returns the fully qualified class name as a symbol.

The singleton property is only defined for singleton classes. It returns the one and
only instance of the class.
10.8.3 Fields
A class may contain any number of field definitions, which represent the class data. Each
field is defined as a typed variable. For example,
class Car {
string make;
string model;
int year;
}
defines a class that has three fields.
10.8.3.1
Field Qualifiers
A field may have the following qualifiers:

A public field is universally visible (and hence accessible).

A protected field is only visible to the class and all its subclasses.

A private field is only visible to the class itself.

A static field is shared across all class instances. You can think of a static field as a
global variable that’s separate from class instances, but can only be accessed by them.

A final field must have a defined value and this value can’t be modified.

A getable field signifies that JavaGram will automatically generate a ‘get’ method for
it, unless one has been explicitly defined by the programmer (e.g., getName() for a
field called name).

A setable field signifies that JavaGram will automatically generate a ‘set’ (as well as
a ‘get’) method for it, unless one has been explicitly defined by the programmer (e.g.,
setName(...) for a field called name).

A delayed field is one that is initialized after the class constructor (if any) has
completed execution (ordinarily, class fields are initialized before any constructor is
invoked). A delayed field cannot be static.
Core Reference
377

A generated field is one that has been generated by a Wizard.

A transient field is not transmitted between client and server when a non-static
remote method of a class is invoked (null is transmitted instead for such fields and is
ignored on the receiving end). Use this qualifier to improve client-server
communication efficiency by excluding potentially large fields (e.g., house-keeping
data) that are not persistent. Note, however, that if a class instance is passed as a
remote method argument or returned by a remote method, it is fully transmitted,
including its transient fields!
You can use a combination of these qualifiers to define a field, so long as the
combination is meaningful. Examples:
public static final int SIZE = 128; // a public constant
public private real r = 1.0;
// invalid: can’t be public and private
A field whose visibility is not explicitly defined, has domain visibility – it’s visible to all
other classes in the same domain.
10.8.3.2
Field Initialization
As shown above, you can initialize a field using a compatible value, in the same manner
you would initialize a local variable. For static fields, the initialization is performed
when the field is accessed for the first time. For non-static fields, initialization is
performed when a class instance is created, but before the class constructor (if any) is
executed, unless the field is delayed, in which case it’s initialized afterwards. Any uninitialized fields are automatically set to null.
When initializing a field, you can refer to other static fields, because these are accessible
prior to object creation. Example:
static final int WIDTH = 100;
// ok
static final int HEIGHT = WIDTH * 2; // ok
int area = WIDTH * HEIGHT;
// ok
real factor = area / 10.0;
// invalid: refers to non-static area
Car car = new Car();
// ok
When initializing a field, you can use the = or the @= operator. The latter casts the
initialization expression to the type of the field before performing the assignment.
Example:
map<symbol, real> prices @= map();
10.8.4 Methods
The behavior of a class is defined by its methods. These represent things that you can do
to a class instance. Example:
class Car {
378
JavaGram Agile Development
protected string make;
protected string model;
protected int year;
public Car (string make, string model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
public string format () {
return $"{make}, {model}, {year}";
}
}
10.8.4.1
Method Qualifiers
A method may have the following qualifiers:

A public method is universally visible (and hence accessible).

A protected method is only visible to the class and all its subclasses.

A private method is only visible to the class itself.

A static method can be invoked directly on the class, without having an instance of
it. Within a static method, you don’t have access to the implicit class instance (i.e.,
this is not defined). Likewise, you don’t have access to non-static class fields.
However, you can access static fields.

A final method is one that can’t be overridden by a subclass.

An abstract method has no definition – its definition is deferred to subclasses.

A synchronized method is one whose entire body is implicitly synchronized by
locking the object instance. Therefore, a synchronized method can’t be static.

A clocal method is intended for execution on the client side. Any attempt to invoke it
on the server side will result in an error. This qualifier has no effect in standalone
deployment.

An slocal method is intended for execution on the server side. Any attempt to invoke
it on the client side will result in an error. This qualifier has no effect in standalone
deployment.

A remote method is designated for execution on the server side even when it’s called
by the client side, unless the program in deployed in standalone mode. When you
invoke a remote method from a client side, the call is automatically serialized and
sent to the server, where the actual call is performed, and the result is serialized and
sent back to the client. If a remote method is called on the server side, it’s treated as a
local call.

A generated method is one that has been generated by a Wizard.
Core Reference
379
A method whose visibility is not explicitly defined, has domain visibility – it’s visible to
all other classes in the same domain.
10.8.4.2
Constructors
Constructors are special methods to be invoked when an object is created using the new
operator. The purpose of a constructor is to initialize the object and do any pre-processing
necessary. When defining a constructor you should not specify a return type (the return
type is implicit and is the class itself), and you should use the class name as the method
name. Example:
class Car {
protected string make;
protected string model;
protected int year;
public Car (string make, string model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
//...
}
The constructor for Car takes three arguments, which simply provide values for the
corresponding class fields, and uses them to initialize the fields. With this definition in
place, we can instantiate Car like this:
Car c = new Car("Toyota", "Camry", 2008);
This causes an instance of Car to be created and the constructor invoked on that instance.
10.8.4.3
Invoking Methods
A non-static method can only be invoked on a class instance. However, a non-static
class member can invoke a class method without specifying an instance. In this case, the
class instance is implicit (this) and need not be specified. A static method can be
invoked directly, without having a class instance. Example:
class Test {
public void foo1 () {
//...
}
public void foo2 () {
foo1();
// equivalent to: this.foo1()
Test.foo3();
// OK because foo3() is static
foo3();
// equivalent to: Test.foo3()
}
public static void foo3 () {
foo1();
// invalid: no class instance specified
Test test = new Test();
380
JavaGram Agile Development
test.foo1();
// OK
}
}
10.8.4.4
This
Within a non-static class method, you can use the keyword this to refer to the implicit
object with which the method has been invoked. In most cases, you don’t need to use
this – you can just refer to class members freely – but where this may cause a name
conflict with other variables (such as in the constructor for Car), using this is necessary.
10.8.4.5
Default Arguments
When you define a method, you can specify default values for its parameters. All default
arguments must be trailing. Example:
public vector find (map criteria, int limit = 0, boolean sort = true) {
vector res = vector();
//
return res;
}
This method takes three arguments, of which two have default values. Here are some
examples of invoking the method:
find(m);
// equivalent to find(m, 0, true)
find(m, 100);
// equivalent to find(m, 100, true)
find(m, 100, false);
find(m, false);
// invalid: can’t omit intermediate parameter limit
Default arguments serve as a convenience, helping you make your code more succinct. A
default argument value can be any valid expression. For non-static methods, it can ever
refer to this.
You can use the = or the @= operator when specifying a default value for a method
parameter. The latter casts the default value to the type of the parameter.
10.8.4.6
Variable Number of Arguments
Sometimes it’s desirable to define a method whose number of arguments can’t be
determined in advance. In such cases, you can leave the method parameters unspecified
(using ellipses) and then use the arg operator to access them at runtime. Example:
public static real max (...) {
if (arg(null) == 0)
return null;
real max @= arg(0);
for (int i = 1, n @= arg(null); i < n; ++i) {
real val @= arg(i);
if (val > max)
max = val;
Core Reference
381
}
return max;
}
You can use arg(null) or arg(-1) to get the actual number of arguments passed to the
method, arg(0) to get the first argument value, arg(1) to get the second, and so on. In
fact, you can use the arg operator in any method, but in normal methods, named
parameters are preferred as they’re self-documenting.
10.8.4.7
Method Overloading
Like Java, JavaGram allows you to overload method names, in the sense that you can
define multiple methods in the same class that have the same name but different
parameter lists. Example:
class Catalog {
public void add (Item item) {
//
}
public void add (vector<Item> items) {
//
}
}
Method overloading is especially useful in writing constructors, as it allows you to offer a
variety of styles of creating class instances. However, before you overload a method, you
should consider whether your objectives can be met with default arguments. The latter
sometimes offers similar flexibility with less coding effort.
10.8.4.8
Type Checking
When you invoke a method, the argument values are type checked against the method
parameter types. If necessary, JavaGram will perform certain implicit type conversions to
achieve a match. For example, if you pass an integer value for a parameter that’s
specified to be real, JavaGram will automatically convert the integer to real, but it will
not do the opposite. If no match can be achieved then the call will be reported as an error.
If multiple methods match the call then javaGram will reconsider those methods and
insist on an exact match (with no implicit type conversion). If the match can’t be
narrowed down to one then JavaGram will report the call as ambiguous.
This style of type checking, which is consistent with how Java operates, is intended to
prevent the programmer from making inadvertent errors in method calls.
10.8.4.9
Remote Methods
Remote methods (and remote classes, described later on) represent one of the most
powerful features of JavaGram. They make the task of writing client-server programs
exceptionally easy by removing the burden of having to deal with data communication,
synchronization, hand-shaking, error handling, and so on. As a result, invoking a remote
method on a server becomes as easy as invoking a local method. Hiding all this
382
JavaGram Agile Development
complexity has the added benefit of allowing you to easily use the same code in different
deployment models.
To make a method remote, use the remote qualifier. When defining a remote method pay
close attention to whether it should be static or not. Static remote methods are more
efficient because there is no class object involved. For a non-static remote method,
JavaGram serializes and sends the object (on which the method is invoked) to the server
side. When the call is completed, this object (which may have been modified) is
serialized, sent back to the client, and any changes to it are reflected back in the original
object. However, GUI fields of the object (if any) are excluded from this update process,
as they would be meaningless on the server side.
Example:
class RemoteTest {
public static void localMethod (string s) {
sys.println($"localMethod({s}) invoked");
}
public remote static void remoteMethod (string s) {
sys.println($"remoteMethod({s}) invoked");
}
public static void main () {
RemoteTest.localMethod("first");
RemoteTest.remoteMethod("second");
}
}
If you run this program in standalone mode, you’ll get this output:
localMethod(first) invoked
remoteMethod(second) invoked
If you run the program in client-server mode, you’ll get these outputs:
Client-side output:
localMethod(first) invoked
Server-side output:
remoteMethod(second) invoked
Whether a method is defined as local or remote is an important design consideration.
Generally, it’s a good practice to have all your database handling on the server-side as
remote, so that data access is completely hidden from the client-side. Also, any logic
that’s intended to be hidden from the client-side (e.g., for security reasons) is best
implemented as remote.
When JavaGram code is demand-downloaded from a server to a client, the
implementation of remote methods is excluded. This improves security and reduces
traffic.
Core Reference
383
10.8.4.10 Targeted Remote Calls
By default, the server against which a remote call is executed is the one from which the
method’s class has been downloaded – we call this the class’s source server. This is
sufficient and secure for the following situations:

When a client runs against a single server. In this case, all remote calls simply resolve
to this server.

When a client runs against multiple servers whose code basses are mutually
exclusive. In other words, each server performs a different set of services. In this
case, <load> markups indicate the target servers via their src property.
A third possibility is when we want to call the same remote method against multiple
servers that have the same code base. In this case, the call must explicitly specify the
intended server, otherwise all such calls will be routed to the source server. The syntax
for a targeted remote call is as follows.
ServerStream::RemoteCall
Here is an example:
class Foo {
public remote static void test (string str) {
sys.println($"in test({str})");
}
public static void main () {
stream s = sys.client("localhost", 444, null, "C:/JagClient/ssl/JAG.jks");
test("first call");
null::test("second call");
s::test("third call");
sys.call(s, Foo, $test, "fourth call");
sys.close(s);
sys.println("end");
}
}
If this is run against a localhost:443 server, the first and second call to test() will be
routed to the class’s source server, and the third and fourth call to localhost:444. Note
that a targeted remote call on a null stream is routed to the class’s source server.
10.8.4.11 Local Methods
If a method is not declared as remote then it’s local. A local method simply executes
where it resides. However, because JavaGram scripts are automatically downloaded from
a server, the same code (and hence method) can reside in both clients and servers. This is
usually desirable because we often need the same logic in both places. In cases where this
is undesirable, you can control the access at a method level.
384
JavaGram Agile Development
To restrict a method’s visibility to client-side only, declare it as clocal. Similarly, to
restrict a method’s visibility to server-side only, declare it as slocal. For maximum
security, JavaGram enforces these qualifiers by excluding a method from the compiled
code not intended for a side.
10.8.5 Inheritance
Inheritance is the cornerstone of object-oriented programming. It allows you to customize
the behavior of an existing class to your specific needs. Example:
class Party {
//...
}
class Person extends Party {
//...
}
class Organization extends Party {
//...
}
Here, Party is called a base or super class, and Person and Organization are called
derived or sub classes. The general rule is that a subclass inherits all the fields and
methods of its super classes, but only has access to those that are not private. Also, any
methods redefined in a subclass override the ones in the super class.
10.8.5.1
Abstract Methods
Abstract methods allow you to declare methods in a super class and defer their
implementation to subclasses. A class that has abstract methods (or extends an abstract
class without implementing all its abstract methods) must be declared as abstract.
Example:
abstract class Shape {
abstract public void draw ();
abstract public void offset (int x, int y);
}
class Rectangle extends Shape {
int x, y;
int width, height;
public void draw () {
//...
}
public void offset (int x, int y) {
//...
}
}
Core Reference
385
10.8.5.2
Polymorphism
An important feature of inheritance is polymorphism. If a method in a class hierarchy is
overridden by a subclass, a call to that method is resolved at runtime by considering the
type of the object on which it’s invoked. For example, consider a subclass of Rectangle
which overrides the draw() method:
class RoundedRectangle extends Rectangle {
real curvature;
public void draw () {
//...
}
}
Polymorphism results in the following behavior:
Shape s = new Rectangle();
s.draw();
// invokes Rectangle.draw()
s = new RoundedRectangle();
s.draw();
// invokes RoundedRectangle.draw()
10.8.5.3
Super
Within a non-static method of a subclass, you can use the super keyword to refer to the
members of the super class. Example:
class RoundedRectangle extends Rectangle {
//...
public void draw () {
super.draw(); // refers to Rectangle.draw()
//...
}
}
10.8.5.4
Mutual Classes
Mutual classes facilitate multiple inheritance – a derived class can have multiple base
classes, provided at most one of them is non-mutual.
Example:
mutual abstract class Shape {
Color color;
abstract public void draw ();
abstract public void offset (int x, int y);
//...
}
class Region {
//...
}
386
JavaGram Agile Development
class Polygon extends Shape, Region {
//...
public void draw () {
//...
}
public void offset (int x, int y) {
//...
}
}
If a mutual class appears more than once in an inheritance hierarchy, then only one
instance of it will appear in the subclass’s object. In this respect, mutual classes behave
the same way as virtual base classes in C++. For example, in
mutual class A {
//...
}
mutual class B extends A {
//...
}
class D extends A, B {
//...
}
An instance of D will contain one instance of A, even though the latter appears twice in the
inheritance hierarchy of D.
As a general rule, all the base classes of a mutual class must also be mutual.
10.8.5.5
Subclass Constructor Rules
When you define a constructor for a subclass, there are certain rules that you need to
observe:

If a direct super class has constructors then you must explicitly invoke one of the
constructors at the beginning of the subclass constructor. However, if the super class
has a default constructor (i.e., a constructor that takes no arguments) then you don’t
need to do this – JavaGram will automatically do this for you.

Alternatively, you can invoke another constructor of the subclass.

If the subclass has multiple direct super classes then you must invoke a constructor of
each super class (that has no default constructor) using casting to avoid ambiguity.

If all the super classes have default constructors and you don’t define a constructor
for the subclass then JavaGram will create one for you which will call the super class
constructors.
Example 1:
Core Reference
387
mutual class Base1 {
public Base1 (string s) {
//...
}
}
class Derived1 extends Base1 {
public Derived1 (string s) {
super(s);
// call to base class constructor necessary
}
}
Example 2:
class Base2 {
public Base2 (int i) {
//...
}
}
mutual class Base3 {
public Base3 () {
//...
}
}
class Derived2 extends Base1, Base2, Base3 {
public Derived2 (string s, int n) {
super@Base1(s);
// resolves to Base1 constructor
super@Base2(n);
// resolves to Base2 constructor
// super#Base3() is called automatically
}
}
10.8.6 Text Members
A text member is like a method and can be used to handle parameterized text. It’s
defined using a markup notation, and can be prefixed with method qualifiers. Text
members may be specified with any of the qualifiers allowed for methods. Example:
static public
<text string newAccEmail (string name, string id, string passwd)>
Dear {name},
This is to confirm that your account has been setup.
Your Userid is: {id}
Your Password is: {passwd}
You will be required to reset your password when you first login.
</text>
The definition must always follow this pattern:
388
JavaGram Agile Development
<text signature properties>
...
</text>
The signature must declare a method that returns a string, but can take arbitrary
parameters. Within the text block, you can write arbitrary expressions enclosed within a
pair of braces. These expressions can refer to the text method parameters. When the
method is called, the embedded expressions are evaluated and spliced into the text, and
the resulting text is returned. This is somewhat similar to delayed strings, but text
methods are more flexible in that the text can span across multiple lines and have
additional properties.
The call
sys.println(newAccEmail("John Smith", "john", "xyz21!"));
to the above text method will return the following text:
Dear John Smith,
This is to confirm that your account has been setup.
Your Userid is: john
Your Password is: xyz21!
You will be required to reset your password when you first login.
10.8.6.1
Text Properties
A text property is specified using the usual markup notation for properties:
propName = propValue
If propValue is not a literal then it must be written as {expression}. The following text
properties are supported:

The trim property can be set to true or false (defaults to false). When set to true, it
trims line breaks, carriage returns, and tabs (but not spaces) from the final text.

The indent property can be set to true or false (defaults to false). When false, it takes
note of the indentation of the first line (counts spaces and tabs). It then removes the
same amount of indentation from every text line. This has the effect of cancelling the
unwanted indentation introduced as a result of how JavaGram code is formatted.

The syntax property can be set to $text or $sql to denote the syntax of the actual text.
10.8.6.2
Text Qualification
The text notation is extensible through qualification. The SQL package (see Chapter 12)
makes extensive use of this to provide qualified text markup such as:
<text.sql ... />
Core Reference
389
<text.sql.query ... />
<text.sql.prepare ... />
Qualified text markup makes it possible to customize text behavior and implement
additional properties. To implement your own text markup qualification, follow the steps
described in Section 9.3.
10.8.7 GUI Members
Like text members, GUI members are defined using a markup notation. These are
described in the next chapter. To implement your own GUI markup extension, follow the
steps described in Section 9.2.
10.8.8 Static Initialization Blocks
As in Java, static initialization blocks are used to perform computations before a class is
instantiated. A static initialization block must appear inside a class, start with the
keyword static, followed by a block of code. Example:
class SalesCatalog {
static map<string, Product> catalog;
static vector<Product> lookupProducts () {
// Lookup and return all products in the database
}
static {
for (Product prod in lookupProducts())
catalog[prod.getId()] = prod;
}
}
Note that code within a static block can’t refer to this; it can only refer to static class
members. The position of a static block in a class is not significant. However, if there are
multiple static blocks in the same class, they are executed in the relative order they
appear.
10.8.9 Singleton Classes
Some classes are not intended to be instantiated more than once. By declaring such
classes as singleton, you ensure that this behavior is enforced. Example:
singleton class PriceList {
protected map items = map();
public void addItem (string name, real price) {
items[name] = price;
}
public real priceOf (string name) {
if (items[name] == null)
throw new Exception("no such item: " + name);
390
JavaGram Agile Development
return items[name]@real;
}
public int countItems () {
return sys.length(items);
}
}
You can refer to the single instance of this class as PriceList.singleton. The following
rules apply:

If a singleton class has no constructor or it has a default constructor (i.e., a constructor
that takes no arguments) then you don’t need to create an instance of the class. The
instance is created automatically the first time you use the .singleton notation.

If a singleton class has one or more constructors that take arguments and no default
constructor then you must create an instance of the class before using the .singleton
notation.

A class derived from a singleton is not singleton unless explicitly defined to be so.

Attempting to create more than one instance of a singleton class results in a runtime
error.

Using the .singleton notation on a non-singleton class results in a load time error.
10.8.10
Remote Classes
A remote class is one whose instances always reside on the server-side, but whose public
members can be accessed from the client side. This can be useful in a number of
situations:

When we don’t want to give client-side access to the internal implementation of the
object due to, for example, security or intellectual property concerns.

The object is potentially too large and costly to be transported between the server and
a client.

The object requires access to server-side resources and can’t be operated if it resides
on the client-side.
You define a class as remote by placing the remote qualifier before its definition.
Example:
remote class Account {
string id;
//...
public string getId () {
return id;
}
public real getBalance () {
//...
}
Core Reference
391
public void deposit (real amount) {
//...
}
public static Account find (int id) {
// Find and return the account
}
}
The usual pattern for getting hold of a remote object is to have a static method in the
remote class (such as find() above) or a remote method in another class that returns it.
The server, however, never returns the remote object, but a reference that will uniquely
identify the object from then on. On the client-side, this reference is provided as a proxy
object. Any method invoked on the proxy object is transmitted to the server side and
performed on the real object instead. Here is some sample client-side code for the above
class:
Account acc = Account.find(101);
if (acc.getBalance() < 0)
acc.deposit(100.0);
As illustrated here, there is no syntactic difference between working with a proxy object
or a real object. JavaGram hides the underlying complexity from the programmer.
10.9 Exceptions
As in Java, the best way to report an error situation is to raise an exception. The
Exception class is predefined and has the following simple definition:
class Exception {
protected string message;
public Exception () {}
public Exception (string msg) {message = msg;}
public string getMessage () {return message;}
}
To define your own exception, you should subclass this class. Example:
class InvalidRecordException extends Exception {
//...
}
Because Exception has a default constructor, you don’t need to define a constructor for
your exception class unless, of course, you need one.
An exception class is typically instantiated for the purpose of raising an exception.
Example:
throw new InvalidRecordException();
392
JavaGram Agile Development
10.10 The Sys Pseudo Class
The sys pseudo class provides a set of useful attributes and methods that you’ll use in
almost any JavaGram program. We call sys a pseudo class because you can’t instantiate
or subclass it.
Due to Flash security restrictions, not all sys methods are available in the Flash JavaGram
runtime. Where a method is not available, it’s marked with the icon . However, if the
method is available in Adobe AIR, it’s marked with the icon
.
10.10.1
Sys Attributes
System attributes denote useful values that are either set internally or at a global level
using runtime options in the command line (see Chapter 7). Unless specified otherwise,
system attributes are read-only.
stream sys.in
stream sys.out
stream sys.err
Summary: These three attributes represent the standard streams for, respectively,
input, output, and error.
sys.println(sys.out, "hello");
Example:
sys.println(sys.err, "failed");
string s = sys.readln(sys.in);
string sys.root
Summary: Denotes the root directory path. JavaGram assumes that all relative paths
are relative to this directory. In a Flash client, there is not root directory, so
this attribute is set to "".
string sys.airRoot
Summary: Denotes the root directory path for a Flash AIR client, which is usually set
to where the AIR application is installed. Otherwise, it is set to "".
string sys.host
Summary: Denotes the server host address (in the format host:port), where host may
be specified as a domain name or an IP address.
string sys.ssl
Summary: Denotes the Java SLL keystore for a client. In a Flash client, this attribute is
always set to null.
string sys.boot
Core Reference
393
Summary:
Denotes the initial script for a client (i.e., the script that enables a client to
boot itself).
map sys.config
Summary: Denotes the contents of the server configuration file. It maps configuration
symbols to values. On client side, this attribute is always set to null.
boolean sys.debug
Summary: When true, it indicates that JavaGram is running in debug mode, allowing a
debugger to set breakpoints and interrogate the JavaGram runtime stack. In
a Flash client, this attribute is always set to null.This attribute is used by
JADE.
boolean sys.verbose
Summary: When true, it indicates that JavaGram is running in verbose mode, causing
additional runtime diagnostics to be sent to standard output (defaults to
false). This attribute can be assigned to.
boolean sys.warning
Summary: When true, warning messages are displayed (defaults to true). This
attribute can be assigned to.
symbol sys.stage
Summary: Denotes the stage of the application, which can be one of: $dev, $test, or
$prod (default is $dev).
vague sys.handy
Summary: Handy attribute (which can be assigned to) for holding application-specific
information. For example, this is used by JAG.swf for retrieving URL
parameters.
string sys.logfile
Summary: Denotes the path of the JavaGram log file. When a log file is specified,
anything sent to standard output or standard error is redirected to this file.
Log files are typically used for servers. In a Flash client, this attribute is
always set to null.
string sys.tz
Summary: Denotes the current timezone as a string (e.g., "America/Los_Angeles"). In a
Flash client, this attribute is always set to null.
stream sys.loader
394
JavaGram Agile Development
Summary:
Denotes the client-side stream that connects the client to its server. This
stream is used for downloading scripts, hence the term ‘loader’.
map sys.xfr
Summary: Denotes a map for controlling the client-server data transfer rate.
[$block=>1024, $sleep=>200]
Example:
Causes data transfer to be in blocks of 1k each, with a delay of 200
milliseconds in between. By adjusting these values you can emulate a slow
connection, such as a modem, for testing purposes. In a Flash client, this
attribute is always set to null.
boolean sys.async
Summary: On the server side, this attribute defaults to true if the server is configured
as an asynchronous server, and false otherwise. On the client side, it
defaults to true if the client is a Flash client or it’s running against an
asynchronous server, and false otherwise. This attribute is typically used as
the condition for writing guarded asynchronous calls so that such calls are
handled asynchronously only when the capability is there.
boolean sys.flash
Summary: Defaults to true for a Flash client, and false otherwise. This attribute is
typically used to avoid accessing JavaGram features that are not supported
by a Flash client (e.g., access to local files, which is barred by Flash
security).
boolean sys.air
Summary: Defaults to true for a Flash AIR client, and false otherwise.
10.10.2
Sys Methods
10.10.2.1
Parsing and Evaluation
vague sys.parse (string code, boolean expr = true, boolean eval = false)
Summary: Parses and analyzes the code denoted by the string code. If successful, the
returned result is ready for evaluation. When expr is true, the code is
treated as an expression. Otherwise it’s treated as a statement. When eval is
true, the result of parsing is immediately evaluated and returned.
Note: sys.parse(code, true, true) is not the same as
sys.eval(sys.parse(code, true)). In the former, code can refer to
variables visible in the method in which the call appears, whereas the latter
will not have access to such variables.
sys.parse("10 + 20")
Example:
Core Reference
395
string sys.serialize (vague object, boolean nullTransient = false)
Summary: Serializes the object denoted by object and returns the resulting string.
When nullTransient is true, all transient fields are output as null.
sys.serialize(obj)
Example:
returns, for example:
[@Employee givenname=>"Craig", lastname=>"Smithers"]
vague sys.eval (vague code)
Summary: Evaluates the code denoted by code and returns the result of evaluation.
sys.eval(sys.parse("10 + 20"))
Example:
returns:
30
10.10.2.2
File Handling
void sys.getDirPath (symbol kind, boolean asUrl = false)
Summary: Returns the path of a standard directory (denoted by kind) which may be
one of: $application, $appStorage, $desktop, $documents, $user. A native
path is returned, unless asUrl is true (asUrl is always ignored in Java).
sys.getDirPath($user)
Example:
void sys.createPath (string path)
Summary: Creates the empty file or directory denoted by path. If path ends in / or \ it
is treated as a directory; otherwise, as a file. To create a directory, this
method will create any non-existent intermediate directories as well. To
create a file, however, this method requires the parent directory to pre-exist.
sys.createPath("C:/handy.txt")
Example:
void sys.deletePath (string path)
Summary: Deletes the file or directory denoted by path. If path ends in / or \ it is
treated as a directory; otherwise, as a file. If path denotes a directory then it
must be empty for it to be deleted.
sys.deletePath("C:/handy.txt")
Example:
void sys.renamePath (string oldPath, string newPath)
Summary: Renames the file or directory denoted by oldPath to newPath. If path ends in
/ or \ it’s treated as a directory; otherwise, as a file.
sys.renamePath("C:/handy.txt", "C:/handy2.txt")
Example:
boolean sys.pathExists (string path)
Summary: Returns true if the file or directory denoted by path exists, and false
otherwise.
sys.pathExists("C:/Program Files")
Example:
396
JavaGram Agile Development
map sys.pathProps (string path, map props = null)
Summary: Returns a map that captures path properties. The map has the following
keys:
 $dir (the directory path)
 $type (one of $dir or $file)
 $length (in bytes)
 $modified (last modified timestamp)
 $readable (true if path is readable). Always returns null on AIR.
 $writeable (true if path is writable). Always returns null on AIR.
When the props parameter is null, a new map is created and returned.
Otherwise, props is updated and returned. Raises an exception if path
doesn’t exist.
sys.pathProps("C:/Program Files")
Example:
returns:
[$dir => "C:/Program Files/", $length => 0,
$modified => [#2008-02-28 16:46:20.17000000], $readable => true,
$type => $dir, $writeable => false]
vector<string> sys.listDir (string path)
Summary: Returns a vector that contains the name of every file or directory inside the
directory denoted by path.
sys.listDir("C:/Program Files")
Example:
returns, for example:
["Adobe", "ahead", "Beyond Compare 2", "Cisco Systems", ...]
void sys.copyFile (string srcFile, string dstFile,
boolean urlSrc = false, symbol callback = null)
Summary: Copies the file denoted by srcFile to the path denoted by dstFile. The
latter is created if it doesn’t exist. When urlSrc is true, srcFile is assumed
to be a URL, otherwise it’s assumed to be a file path. When urlSrc is true
and callback (which may denote a method in the same class, having one
vague parameter) is not null, callback is called upon completion of the
copy and given the path of the target file (when successful) or an exception.
sys.copyFile("C:/handy.txt", "C:/handy2.txt")
Example:
string sys.createTempFile (string prefix, string suffix, string dir = null,
Boolean delOnExit = false)
Core Reference
boo
397
Summary:
Creates a temporary file and returns its path. prefix must be at least 3
characters long. When dir is not specified, the default temporary directory
is used. When delOnExit is specified and is true, the file is deleted when the
application exits. Applications, however, should assume responsibility for
removing temporary files. On AIR, all arguments are ignored and the
temporary file name is determined automatically.
Example:
sys.createTempFile("Test", ".txt")
returns something like:
"C:/temp/Test8217.txt"
stream sys.open (string file, string mode, string encoding = null)
Summary: Opens the file denoted by path in mode and returns a stream for it. The latter
must be "r" (for reading), "w" (for writing), or "a" (for appending). When
encoding is not specified, the default character encoding is used (platform
dependent). Valid encoding values include:
 "US-ASCII" (seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin
block of the Unicode character set).
 "ISO-8859-1" (ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1).
 "UTF-8" (eight-bit UCS Transformation Format).
 "UTF-8WIN" (JavaGram's variant of UTF8 for Windows platforms).
 "UTF-16BE" (sixteen-bit UCS Transformation Format, big-endian byte
order).
 "UTF-16LE" (sixteen-bit UCS Transformation Format, little-endian byte
order).
 "UTF-16" (sixteen-bit UCS Transformation Format, byte order
identified by an optional byte-order mark).
sys.open("C:/handy.txt",
"r")
Example:
void sys.close (stream s)
Summary: Closes the stream denoted by s, which must have been returned by an
earlier call to sys.open().
sys.close(s)
Example:
void sys.zip (string zipFilePath, string zipRoot, ...)
398
JavaGram Agile Development
Summary:
Creates the ZIP file denoted by zipFilePath and adds the files/directories
denoted by the third argument onward to the ZIP file. If zipRoot is null
then the files/directories must be specified using their absolute path (the
entry names in the ZIP file will also be named according to these absolute
paths). Otherwise, they should be specified relative to zipRoot (the entry
names in the ZIP file will also be named according to these relative paths).
Any directory whose name starts with a period (e.g., ‘.svn’) is ignored.
Each ZIP entry must be either a file name or a vector of file names (not
directories). The third argument may optionally be a map, in which case it’s
not treated as a ZIP entry but as a means for renaming the ZIP entries. Each
ZIP entry is looked up in this map to get its new name. If present in the
map, the file is accessed using its original name and the ZIP entry is written
using the new name. Otherwise, the original file name is used for both
purposes.
Example:
sys.zip("C:/handy.zip", "C:/proj/", "crm/", "sample.txt")
vector<string> sys.unzip (string zipFilePath, string toDirPath = null)
Summary: Unzips the ZIP file denoted by zipFilePath into the directory denoted by
toDirPath. The latter may be null, in which case the file is not unzipped but
its content is returned as a vector, where each element is the path of an
entry.
sys.unzip("C:/handy.zip", "C:/tmp")
Example:
void sys.load (vague files, boolean relative = true, boolean autoCallMain = false)
Summary: Loads the files denoted by files. The latter may be a file path or a vector
of file paths. When relative is true, the file paths are assumed to be relative
to sys.root; otherwise, they are treated as absolute file paths. Loading
each file causes it to be parsed and analyzed, ready for execution. When
autoCallMain is true, the public static void main() method of each class
(if defined) is executed upon successful loading.
When specifying a file, don’t include its extension. For example, instead of
Client.jag (or Client.jax) use Client. JavaGram will use the compiled
version if it exists. If the source file also exists and is more recent or older
than the compiled version, JavaGram will delete the compiled version and
use the source version instead.
sys.load("crm/Client")
Example:
void sys.jload (string path)
Summary: Causes Java classes to be loaded as if they were part of the CLASSPATH the
program was started with. path must denote a directory of Java class files
or a JAR file.
sys.jload("C:/drivers/MyDriver.jar")
Example:
Core Reference
399
10.10.2.3
Input/Output
vague sys.read (stream s = null)
Summary: Reads, parses, analyzes, and returns the next JavaGram expression from the
stream denoted by s (or sys.in when s is null). When there is nothing left
to read (i.e., end of file is reached), null is returned. The returned
expression is ready for evaluation.
sys.read(s)
Example:
string sys.readln (stream s = null, boolean incEOL = true)
Summary: Reads and returns the next line from the stream denoted by s (or sys.in
when s is null). When incEOL is true, EOL (if present) is included in the
returned string. Otherwise, it’s excluded.
sys.readln(sys.in, false)
Example:
native sys.readbin (string file)
Summary: Reads and returns a binary object that represents the contents of the file
whose path is denoted by file.
sys.readbin("C:/handy/Logo.png")
Example:
void sys.skip (stream s, int bytes)
Summary: Skips the number of bytes denoted by bytes in the input stream s.
sys.skip(s, 64)
Example:
void sys.pp ([stream s], ...)
Summary: Pretty-prints values denoted by the arguments to the stream denoted by s,
or sys.out when s is not specified. Note that this method does not flush the
output buffer, so unless you call sys.flush(), no output may appear. Unlike
sys.print(), this method:
 Escapes special characters (\n, \r, \t, \b, \f, ', ", and \).
 Outputs characters and strings with their begin/end quotation marks
included.
 Treats circular references safely to avoid infinite recursion.
sys.pp(vec)
Example:
void sys.ppEscape (stream s, vague val, boolean escapeHtml = true)
Summary: Similar to sys.pp() except that it always expects an output stream and a
single value. When escapeHtml is true:
 & is output as &amp;
 < is output as &lt;
 > is output as &gt;
sys.ppEscape(sys.out, "<p>Test</p>", true)
Example:
400
JavaGram Agile Development
void sys.ppln ([stream s], ...)
Summary: Same as sys.pp() except that the output is followed by a new line and
flushed.
sys.ppln(vec)
Example:
void sys.print ([stream s], ...)
Summary: Outputs values denoted by the arguments to the stream denoted by s, or
sys.out when s is not specified. Note that this method does not flush the
output buffer, so unless you call sys.flush(), no output may appear.
sys.print("balance is ", s)
Example:
void sys.println ([stream s], ...)
Summary: Same as sys.print() except that the output is followed by a new line and
flushed.
sys.println("balance is ", s)
Example:
void sys.printEscape (stream s, vague val, boolean escapeHtml = true)
Summary: This is the ‘print’ version of sys.ppEscape(). When escapeHtml is false,
backslash-escaping is performed on special characters and backslash itself.
sys.printEscape(sys.out, "<p>Test</p>", true)
Example:
void sys.flush (stream s = null)
Summary: Flushes any output pending on the stream denoted by s, or sys.out if no
stream is specified.
sys.flush()
Example:
void sys.xmlExport (stream s, vague val, string rootTag = null,
boolean header = true, boolean indent = true)
Summary: Converts the value denoted by val to XML format and writes it to the
stream denoted by s. When rootTag is not specified, the root tag will be
<root>. When header is true, an <?xml...?> header is written to s first.
When indent is true, the XML is properly indented.
sys.xmlExport(sys.out, expr)
Example:
vague sys.xmlImport (vague source)
Summary: Converts the XML provided by source (which may be an input stream or a
string) to a JavaGram literal, and returns it.
sys.xmlImport(str)
Example:
10.10.2.4
Debugging
string sys.stackTrace ()
Core Reference
401
Summary:
Example:
Returns the current thread’s list of stack frames as a string. Each line in the
string represents the stack frame of a method call, starting with the current
method and going back all the way to a main method.
A call will return something like this:
[2] DataSet.sample(2.3, 452.89) called at
C:/JavaGram/jag/test/code/Basic2.jag (line 5 col 2-8)
[1] DataSet.process(vector) called at
C:/JavaGram/jag/test/code/Basic2.jag (line 13 col 5-12)
[0] DataSet.main() called at C:/JavaGram/jag/test/code/Basic2.jag
(line 3 col 0-5)
10.10.2.5
String Handling
stream sys.buffer ()
Summary: Creates and returns a new buffer stream. This buffer can be used for IO
purposes the same way file and channel streams are used.
sys.buffer()
Example:
void sys.setBufStr (stream buffer, string str)
Summary: Sets the contents of the buffer stream denoted by buffer to the string
denoted by str.
sys.setBufStr(buf, "sample data")
Example:
string sys.getBufStr (stream buffer)
Summary: Returns the contents of the buffer stream denoted by buffer as a string.
sys.getBufStr(s)
Example:
string sys.fileAsHex (string file)
Summary: Reads the binary contents of the file whose path is denoted by file, and
returns a hexadecimal representation of the data as a string.
sys.fileAsHex("C:/handy/Logo.png")
Example:
string sys.format (vague obj, string format = null)
Summary: This method is used for formatting numbers, dates, strings, and symbols.
Default formatting (where format is null) works as follows. Integers are
formatted by a comma appearing between every three digits (e.g., 100,000).
Reals are formatted the same way but also rounded to two decimal places
(e.g., 100,000.25). Dates are formatted as, for example, 2008-10-22.
Symbols are stripped of their initial $ char. Strings are not altered.
Where the default behavior is insufficient, use an explicit format string to
specify your desired format, as exemplified below.
402
JavaGram Agile Development
Example:
sys.format(10000, "0:000")
gives: 10:000
sys.format(10000.25282, "0,000.000")
gives: 10,000.253
sys.format(sys.date(),"dd-MM-yyyy HH:mm:ss.SSS")
gives: 18-02-2008 15:43:31.206
sys.format($action, "name")
gives: "Action"
sys.format($action, "upper")
gives: "ACTION"
sys.format("SIZE", "lower")
gives: "size"
int sys.strCmp (string s1, string s2, boolean ignoreCase = false)
Summary: Compares strings s1 and s2, and returns zero if they’re equal, a negative
integer if s1 is lexicographically less than s2, and a positive integer
otherwise. The comparison is case-sensitive unless you pass true for
ignoreCase.
sys.strCmp("one", "two")
Example:
returns: -5
boolean sys.strLike (string s1, string s2, boolean ignoreCase = false)
Summary: Returns true if s1 is a prefix of s2. The comparison is case-sensitive unless
you pass true for ignoreCase.
sys.strLike("man", "manner")
Example:
returns: true
char sys.nthChar (string str, int idx)
Summary: Returns the character in str denoted by the zero-based index idx.
sys.nthChar("sample", 2)
Example:
returns: 'm'
int sys.strChar (string str, char ch, boolean reverse = false)
Summary: Returns the zero-based index for the first (or last, if reverse is true)
occurrence of ch in str, and null otherwise.
sys.strChar("sample", 'm')
Example:
returns: 2
string sys.strTo (string str, symbol to)
Summary: Returns a string that is the result of transforming str according to the
format denoted by to. The latter may be one of: $lower, $upper, or $name.
Core Reference
403
Example:
sys.strTo("Hello", $lower)
returns: "hello"
sys.strTo("Hello", $upper)
returns: "HELLO"
sys.strTo("ele mcpherson", $name)
returns: Ele McPherson
string sys.strEncode (string str, string encoding)
Summary: Returns a string that is the result of encoding str according to the character
encoding denoted by encoding.
sys.strEncode("…some Unicode text…", "UTF-8")
Example:
string sys.subStr (string str, int from, int to = null)
Summary: Returns a substring of str which starts at the zero-based position denoted
by from and ends at the position denoted by to (or end of the string when to
is null).
sys.subStr("Something", 2, 5)
Example:
returns: "met"
int sys.strHas (string str, string substr, boolean reverse = false)
Summary: Returns a zero-based integer denoting the starting position of substr in str,
if successful, and null otherwise. When reverse is true, the search is
performed from the end of the string.
sys.strHas("Something", "met")
Example:
returns: 2
string sys.strTrim (string str, boolean full = false)
Summary: Returns the result of trimming whitespace from either end of str. When
full is true, whitespace is also removed from within the string.
sys.strTrim(" a lot ", true)
Example:
returns: "alot"
vector<string> sys.strSplit (string str, string separator = null)
Summary: Returns a vector which is the result of splitting str into parts that are
separated by separator. When the latter is null, "." is used.
sys.strSplit("jag.lang.common")
Example:
returns: ["jag", "lang", "common"]
native sys.strPattern (string reg)
Summary: Returns a pattern which is the result of compiling the regular expression
denoted by reg.
sys.strPattern("obj.*s")
Example:
404
JavaGram Agile Development
int sys.strFind (string str, vague reg, int start = 0)
Summary: Checks if there is a match between str and the regular expression denoted
by reg, which may be a regular expression string or a compiled pattern
return by sys.strPattern(). The search starts in str at the position denoted
by start. The returned value denotes the zero-based index of the matching
substring, or -1 if there is no match.
sys.strFind("alternatives", "nat.*s")
Example:
returns: 5
boolean sys.strMatch (string str, vague reg)
Summary: Returns true if str matches the regular expression denoted by reg, which
may be a regular expression string or a compiled pattern return by
sys.strPattern().
sys.strMatch("objectives", "obj.*s")
Example:
returns: true
string sys.strReplace (string str, vague reg, string replace, boolean all = false,
vector<string> groups = null)
Summary: Replaces occurrences in str that match the regular expression denoted by
reg (which may be a regular expression string or a compiled pattern return
by sys.strPattern()) with replace. Unless all is false, only the first
occurrence is replaced. When group is not null, all replaced occurrences are
added to group.
vector group = vector();
Example:
sys.strReplace("the last fast dog", ".ast", "xx", true, group)
returns: "the xx xx dog"
and group is set to: ["last", "fast"]
symbol sys.strSym (string str)
Summary: Returns a symbol which is the result of converting str to a symbol.
sys.strSym("my symbol")
Example:
returns: ${my symbol}
string sys.symStr (symbol sym)
Summary: Returns the string part of sym (i.e., excluding the $).
sys.symStr($name)
Example:
returns: "name"
vague sys.cypher (string data, string key, string verify = null)
Core Reference
405
Summary:
If verify is null then it returns a string which is the result of encrypting
data using key. Otherwise, it verifies that the encrypted string (denoted by
verify) matches the result of encrypting data using key, and returns a
boolean result. For security, the encryption is uni-directional: cypher cannot
be used to decrypt an encrypted string, only to verify it. For bi-directional
encryption/decryption, you can do the following:
 Pass "encode" for key and do not use verify. The result of encrypting
data is returned.
 Pass "decode" for key and do not use verify. The result of decrypting
data is returned.
For one-way hash digests of data such as passwords, pass "digest" for the
key. A SHA digest of the string is returned.
Example:
sys.cypher("sample", "key")
returns: "dfncshzfzm"
sys.cypher("sample", "key", "dfncshzfzm")
returns: true
string s = sys.cypher("sample", "encode")@string;
sets s to: "c2FtcGxl"
sys.cypher(s, "decode")
returns: "sample"
sys.cypher("sample", "digest")@string
returns: "ykmiaiqksg"
string sys.normPath (string path, string prefix = null, symbol format=$unix)
Summary: Returns the result of normalizing path, such that backslashes are replaced
by forward slashes. If prefix is specified then it should be a partial path.
This partial path is matched within path and anything to the left of it in path
is discarded. To replace forward slashes with backslashes instead, pass $dos
for format.
sys.normPath("C:\\proj\\crm\\core\\db.java")
Example:
returns: "C:/proj/crm/core/db.java"
sys.normPath("C:\\proj\\crm\\core\\db.java", "proj/crm")
returns: "proj/crm/core/db.java"
string sys.pathConc (...)
Summary: Returns the result of concatenating one or more paths. The separator / is
automatically inserted or removed to ensure a valid outcome.
sys.pathConc("C:/proj", "crm/rep", "sample.html")
Example:
returns: "C:/proj/crm/rep/sample.html"
map<symbol,string> sys.pathParts (string path)
Summary: Returns a map of three elements denoting the directory, the name, and the
file extension of path.
406
JavaGram Agile Development
Example:
sys.pathParts("C:/proj/crm/test.jag")
returns: [$dir => "C:/proj/crm/", $ext => "jag", $name => "test"]
vector<string> sys.htmlRefs(string htmlPath, string parentPath = null)
Summary: Returns a vector of files that are referenced by the HTML file denoted by
htmlPath. If parentPath is specified then this is used to determine how to
resolve “..” in the encountered file paths. parentPath should be usually set
to the relative path of the file, or the relative path of the file in which
htmlPath appears as an anchor.
Currently only image files of the form <img src="..." ...> and HTML
files of the form <a href="..." ...> are considered.
sys.pathParts("C:/doc/help/Two.html", "doc/cue/one.html")
Example:
returns: ["doc/gifs/Logo.gif", "doc/gifs/Heading.gif"]
10.10.2.6
Client-server
void sys.appServer (string host, int port, symbol mode = $sync)
Summary: Creates and runs an application server thread that creates a channel on the
host denoted by host (which must be a valid server name or IP address) and
listens for input on the port denoted by port. Each time a client attempts to
connect to this server, the server creates a separate session thread (with its
own channel, which listens on the same port) and hands the client over to
that thread. mode may be one of
 $sync, which causes each session to handle client requests
synchronously.
 $async, which causes client requests to be handled asynchronously.
This mode uses a separate thread to handle each incoming client
request.
 $blaze, which uses Adobe’s BlazeDS (AMF over HTTP) instead of
sockets. A server of this mode must be deployed in a servlet container
(such as Apache Tomcat). The $blaze mode always implies $async.
The advantage of this mode over sockets is that when Flash clients
reside behind firewall proxy servers, proper HTTP tunneling is
performed.
A socket server thread loops indefinitely. Each session thread also loops
indefinitely until its associated client exits, at which time the session thread
terminates.
A server is configured using the –config command line option (see Chapter
7).
sys.appServer("localhost", 443, $async)
Example:
void sys.proxyServer (string host, int port, symbol mode, vector endServers)
Core Reference
407
Summary:
Creates a proxy server which directs traffic aimed at host:port to one of
the application server hosts specified in endServers. As in appServer, each
client connection results in a separate session. However, the role of the
session is to just pass traffic through to the application server and back.
Pass $failOver for mode if you want the proxy server to run in a failover
mode. Pass $loadBalance for mode if you want the proxy server to load
balance across multiple application servers.
A client has no way of telling a proxy server which end-server it should
connect to. The decision is purely up to the proxy server. A client, in fact,
can’t distinguish a proxy server from an application server.
In attempting to connect to application servers, a failed application server is
not retried for at least a further 5 minutes. Under a failover model, a client
is always connected to the first available application server in the list.
Under a load-balance model, client connections are allocated to available
application servers in a round-robin fashion while attempting to balance the
load according to the weight of each application server.
Each application server is specified as a map that may have the following
keys:
 $host should denote the application server’s host name or IP address.
 $port should denote the port on which the application server listens for
connections.
 $weight should denote the relative weight of the server (defaults to 1.0
if not specified). This must be a real number greater than zero and less
than or equal to 1.0. The weight should reflect the relative capacity of
the server. For example, 0.5 means that the server has half the capacity
of a server that has a weight of 1.0.
 $keystore specifies a keystore (see Chapter 7) and is needed if the
application server uses SSL.
 $timeout is specified in milliseconds (defaults to 15000 milliseconds if
not specified). This timeout applies to each packet in transit through
the proxy server. If a packet times-out and the channel is still healthy,
it’s re-sent until successful. A small timeout here helps the proxy
server to quickly identify dead clients and remove their sessions.
 $delay is specified in milliseconds (defaults to 0 if not specified). This
delay is applied after each packet transmission. You can use it to
emulate the speed of a modem.
Example:
sys.proxyServer("localhost", 444, $loadBalance,
[
[$host => "localhost", $port => 442, $weight => 0.5],
[$host => "localhost", $port => 443, $weight => 1.0]
]
);
stream sys.client (string host, int port, int timeout = null, string keystore = null,
string proxyHost = null, int proxyPort = null, string proxyAuth = null)
408
JavaGram Agile Development
Summary:
Creates a client channel, connects it to the server running on host and
listening on port (host must be a host name or IP address), and returns a
stream for it.
Each request from the client is subject to a timeout (specified in
milliseconds). This defaults to 60,000 milliseconds (i.e., 60 seconds).
If the server against which this client runs uses SLL then you must use the
keystore parameter to specify the keystore holding the certificate. The
following values are allowed:
 null mean don’t use SSL (i.e., normal server).
 "" means the server uses a digitally-signed certificate, so no keystore
specification is required.
 Any other string should give the path to the keystore which holds your
self-signed certificate.
The keystore parameter is usually specified via the –ssl command line
option (see section below on command line options).
In networks where the client application needs to go through a firewall
proxy server to connect to a server outside the network, use proxyHost
(name or IP address) and proxyPort (port number) to specify the proxy
server. Some proxy servers also require authentication, in which case this
should be specified as the last argument (proxyAuth), using the format
"username:password". Don’t confuse a firewall proxy server with a
JavaGram proxy server; they’re not the same thing.
The stream returned by client() can be used for IO purposes in the usual
fashion. To close the stream, call sys.close().
Example:
sys.client("localhost", 443)
void sys.download (string remotePath, string localPath, stream channel = null,
boolean remoteRelative = true, symbol callback = null,
list extraCBArgs = null, boolean noZip = false)
Core Reference
409
Summary:
This method may be used by a client to request a file from a server.
remotePath denotes the server-side file path. localPath denotes the clientside file path, where the received file will be stored. channel denotes the
channel stream between the client and the server (if not specified,
sys.loader will be used instead). The file is sent only if the download
permissions defined in the server configuration permit the transfer.
The optional remoteRelative parameter indicates whether remotePath
should be relative to server-side sys.root.
When the download is complete, callback is called if present. This should
be a method in the same class, and must have at least a vague parameter,
followed by optional parameters if desired (use extraCBArgs to pass values
for the additional parameters as a list of arguments). The first parameter
receives either an Exception or an information map of the form:
[$local=>strPath, $remote=>strPath, $size=>intVal, $timestamp=>intVal]
The server may decide to compress the file for efficient transmission, in
which case, the client will decompress the file upon receiving it. However,
when noZip is true, no compression will be performed.
Note: to force the downloaded file to be added to the partitions cache, pass
an empty string or null for localPath. This is particularly useful in a Flash
client, as the user will no longer be required to nominate a location for
saving the file. Once added to the partitions cache, the file can be accessed
using sys.use().
Because of security restrictions, sys.download()behaves differently in a
Flash client:
 remotePath may be a HTTP or HTTPS URL or a server-side file path.
When it’s a URL, channel and remoteRelative parameters are ignored.
 localPath is used as a suggestion for the local name of the downloaded
file. Also, callback receives the actual file name selected (not its path)
in the $local entry of the info map. Pass an empty string or null if you
want the file to be added to the partitions cache.
 Because of the way the channel protocol works for Flash,
sys.dowload() should always be called in a callback. To force this, you
can call Util.ping() asynchronously and call sys.download() in its
callback.
 When the file is downloaded, the user is prompted with an alert box,
followed by a file browse dialog box to choose a location for saving
the file. This can be avoided by passing null for localPath (see below).
 If the downloaded file is a ZIP file, having just one entry, it’s
automatically unzipped and that entry is saved instead.
 The download is always asynchronous in a Flash client (as opposed to
synchronous in a Java client).
Only the last bullet point applies to an AIR client.
Example:
410
sys.download("Report.rtf", "C:/reports/Report1.rtf")
JavaGram Agile Development
void sys.upload (string localPath, string remotePath, stream channel = null, boolean
remoteRelative = true, symbol callback = null, list extraCBArgs = null)
Summary: This method may be used by a client to push a file to a server. localPath
denotes the client-side file path. remotePath denotes the server-side file
path, where the received file will be stored. channel denotes the channel
stream between the client and the server (if not specified, sys.loader will
be used instead). The file is accepted by the server only if the upload
permissions defined in the server configuration permit the transfer.
The optional remoteRelative parameter indicates whether remotePath
should be treated as relative to server-side sys.root.
When the upload is complete, callback is called if present. This should be
a method in the same class, and must have at least a vague parameter,
followed by optional parameters if desired (use extraCBArgs to pass values
for the additional parameters as a list of arguments). The first parameter
receives either an Exception or an information map of the form:
[$local=>strPath, $remote=>strPath, $size=>intVal, $timestamp=>intVal]
Because of security restrictions, sys.upload() behaves differently in a
Flash client:
 remotePath may be a HTTP or HTTPS URL or a server-side file path.
When it’s a URL, it must denote a web server page that handles the
uploading (in this case, channel and remoteRelative parameters are
ignored).
 The user is presented with a file browse dialog box to choose the file to
be uploaded. You can use localPath to specify a file filter. For
example, "Image Files:*.gif;*.png;*.jpg;*.jpeg" may be used for
selecting common image files (note the use of colon to separate the
prompt from the file extension patterns). When localPath is null, the
selectable file type is based on the extension of remotePath. After the
file is chosen, the name (not path) of the uploaded file is denoted by
the $local key of the callback info map.
 The upload is always asynchronous in a Flash client (as opposed to
synchronous in a Java client).
Only the last bullet point applies to an AIR client.
Example:
sys.upload("C:/reports/Report1.rtf", "Report.rtf")
string sys.use (string file, stream channel = null, boolean, track = true)
Core Reference
411
Summary:
Example:
Demand-downloads the file whose path is denoted by file.
The file is downloaded over channel stream (or sys.loader if channel is not
specified). If channel and sys.loader are not valid streams then the file is
not downloaded and deemed to reside locally. The method returns the
absolute client-side path of the file.
If a hook callback for tracking the downloading of files is defined, it’s
called only when track is true and the file is actually downloaded.
When a file is downloaded from a server, it’s cached on the client-side and
given the same timestamp as on the server-side. In a subsequent run, the
file is re-downloaded only when the timestamps mismatch (implying that
the server-side file has been modified).
Use this method in scripts, for example, to refer to required data files (e.g.,
HTML and GIF files).
A typical use is in the GUI package where a GUI element refers to an
image:
<Button title="Perform" image={sys.use("gifs/Perform.gif")}/>
void sys.rload (vague files, stream channel = null, boolean autoCallMain = true)
Summary: Remote-loads the file(s) denoted by files. The latter may be a file path or a
vector of file paths, all of which must be relative to server-side sys.root.
Remote loading is like local loading (see sys.load()) except that if the file
is not present on the client or its timestamp mismatches the server-side file,
it’s first downloaded. The file is downloaded over the channel stream
denoted by channel (defaults to sys.loader if not specified). When
autoCallMain is true, the main() method of each loaded class (if defined) is
automatically invoked.
sys.rload("crm/Client")
Example:
boolean sys.partition (string name, symbol callback, stream channel = null)
Summary: Downloads the partition denoted by name over the channel stream denoted
by channel (defaults to sys.loader if not specified) and then invokes
callback, unless the partition has already been downloaded by an earlier
call, in which case nothing will happen. The method return true if the
callback is called and false otherwise.
The callback must be a method in the same class with the following
signature:
void callback (string err)
In a non-Flash client, callback may be null.
Because of the way the channel protocol works for Flash, sys.partition()
should always be called in a callback. To force this, you can call
Util.ping() asynchronously and call sys.partition() in its callback.
In standalone mode, the callback is called immediately without attempting
to obtain a partition.
To clear a partition from the local cache, use sys.clear().
Example:
412
sys.partition("Main", $mainPartCB)
JavaGram Agile Development
boolean sys.partitioned (string remotePath, boolean extract = false)
Summary: Returns true if the file denoted by remotePath has been partitioned (i.e., the
file resides in the client’s partitions cache as a result of earlier partition
download(s) or because of an earlier call to sys.download()). When extract
is true and the file exists, the file is extracted from the partition and written
to the location denoted by remotePath, relative to sys.root.
sys.partitioned("crm/Main")
Example:
boolean sys.buildPartition (string absZipPath, vector<string> source)
Summary: Builds a partition (for client-side use) whose absolute path is denoted by
absZipPath and recursively includes the files/directories denoted by source.
The source files/directories must be specified using their relative path, but
no file extension is required. Any directory whose name starts with a period
(e.g., ‘.svn’) is ignored. Each source file is compiled (if an up-to-date
compiled version doesn’t already exist) and then filtered for client-side
consumption. The build process is identical to that used for building
partitions specified in a server configuration file. This method is useful
where a partition needs to be built dynamically and/or programmatically.
sys.buildPartition("C:/JavaGram/parts/MyPart.zip", [...])
Example:
void sys.release (vague proxyObj)
Summary: Intended to be called by a client on a proxy object (has no effect in
standalone mode). Releases the proxy object’s reference to its server-side
remote object, causing the latter’s reference count to be decremented. Any
subsequent attempt to access proxyObj on the client-side will be treated as
an error. If the remote object’s reference count reaches zero and has a nonstatic release() method that takes no arguments, then this method will be
called automatically on the server side, giving you the opportunity to
release any resources held by the object.
As a fallback measure, when a session terminates, all its proxy objects are
automatically released.
sys.release(acc)
Example:
10.10.2.7
Maths
vague sys.min (...)
Summary: Returns the minimum of zero or more quantities. The arguments may be
integers, reals, characters, or dates. For character arguments, the character
code is used. For date arguments, the timestamp equivalent of the date is
used. The returned value maintains its original type. Returns null when no
arguments are provided.
sys.min(10, 20.3, 'a', sys.date())
Example:
returns: 10
Core Reference
413
vague sys.max (...)
Summary: Same as sys.min() except that the maximum quantity is returned.
sys.max(10, 20.3, 'a', sys.date())
Example:
returns: 2008-02-24 13:34:16.582000000
vague sys.toNum (vague value)
Summary: Returns the result of converting value to a number. value may be a string,
date, integer, or real. A date is converted to its numeric equivalent
expressed in milliseconds.
sys.toNum("12.4")
Example:
returns: 12.4
vague sys.trunc (real number, int decimalPlaces = 0)
Summary: Returns the result of truncating number to the number of decimal places
denoted by decimalPlaces.
sys.trunc(12.67569, 2)
Example:
returns: 12.67
vague sys.round (real number, int decimalPlaces = 0)
Summary: Returns the result of rounding number to the number of decimal places
denoted by decimalPlaces. This method uses the ‘round half to even’
method, as specified in http://en.wikipedia.org/wiki/Rounding.
sys.round(12.67569, 2)
Example:
returns: 12.68
real sys.sqrt (vague number)
Summary: Returns the square root of number (which may be an integer, real, or
character).
sys.sqrt(7)
Example:
return: 2.6457513110645907
real sys.log (vague number)
Summary: Returns the natural logarithm of number (which may be an integer, real, or
character).
sys.log('A')
Example:
returns: 4.174387269895637
real sys.sin (vague number)
Summary: Returns the trigonometric sine of number expressed in degrees (which may
be an integer, real, or character).
sys.sin(90)
Example:
returns: 1.0
414
JavaGram Agile Development
real sys.cos (vague number)
Summary: Returns the trigonometric cosine of number expressed in degrees (which
may be an integer, real, or character).
sys.cos(0)
Example:
returns: 1.0
real sys.tan (vague number)
Summary: Returns the trigonometric tangent of number expressed in degrees (which
may be an integer, real, or character).
sys.tan(45)
Example:
returns: 1.0
real sys.distance (real lat1, real lng1, real lat2, real lng2)
Summary: Returns the distance (in meters) between two geo-coordinates specified in
latitude and longitude.
sys.distance(0, 0, 1, 0)
Example:
returns: 111194.92664455873
10.10.2.8
Date
string sys.timezone (string zone = null)
Summary: Returns the current time zone. You can also modify the current time zone
by passing a valid zone string. Valid examples are:
Example:
 "GMT"
 "GMT+10"
 "America/Los_Angeles"
 "America/New_York"
 "Australia/Melbourne"
sys.timezone("Australia/Melbourne")
date sys.date ()
date sys.date (int year, int month, int day, int hour = 0, int minute = 0, int second = 0,
int nano = 0)
date sys.date (int num, boolean pure = false)
date sys.date (string str)
Summary: Returns the specified date. A date can be specified in a variety of formats.
The first version returns the current date/time. The second version returns a
date/time as specified by the arguments. The third version accepts a
numeric date, which is milliseconds since UNIX epoch time (1st of Jan
1970). Similarly, casting a date to int gives its equivalent value in
milliseconds. When pure is true, no time component is returned. The last
version accepts a string date.
Core Reference
415
Example:
sys.date()
returns: 2008-02-25 08:40:09.252000000
sys.date(2000, 0, 22)
returns: 1999-12-22
sys.date(600000000)
returns: 1970-01-08 08:40:00
sys.date("1999-1-22 14:10:55")
returns: 1999-01-22 14:10:55
vague sys.dateParts (date d, map parts = null)
Summary: Returns the individual parts of the date d as a map. When parts is non-null,
this map is modified and returned rather than creating a new map. weekDay
starts on Sunday (=1).
sys.dateParts(sys.date())
Example:
returns:
[$day => 25, $hour => 8, $minute => 45, $month => 2,
$nano => 357000000, $second => 12, $weekDay => 2,
$year => 2008, $yearDay => 56]
void sys.sleep (int millis)
Summary: Causes the current thread to wait for millis milliseconds.
sys.sleep(2000) // wait for 2 seconds
Example:
10.10.2.9
Collections
vector sys.insert (vector vec, int at, vague elem)
Summary: Inserts elem at the position denoted by at in vec, and returns the vector. at
must be in the range 0 to the length of vec.
sys.insert(["one", "two"], 1, "xx")
Example:
returns: ["one", "xx", "two"]
vector sys.append (vector vec, vague elem)
Summary: Appends elem to the end of vec, and returns the vector.
sys.append(["one", "two"], "three")
Example:
returns: ["one", "two", "three"]
list sys.pair (vague head, list tail)
Summary: Creates and returns a list using the specified head and tail.
sys.pair(10, $(20, 30))
Example:
returns: $(10, 20, 30)
vague sys.head (list ls)
Summary: Returns the head of the list denoted by ls. When the latter is null, null is
returned.
416
JavaGram Agile Development
Example:
sys.head($(10, 20, 30))
returns: 10
list sys.tail (list ls)
Summary: Returns the tail of the list denoted by ls. When the latter is null, null is
returned.
sys.tail($(10, 20, 30))
Example:
returns: $(20 30)
list sys.setTail (list ls, list tail)
Summary: Replaces the tail of the list denoted by ls, with tail, and returns the
resulting list. The latter may be null.
sys.setTail($(10, 20, 30), $(100))
Example:
returns: $(10, 100)
vague sys.remove (vague collection, vague elem, symbol scope = $first)
Summary: Removes elem from collection (which may be a vector or map) and returns
the modified collection. If the latter is a map then scope is irrelevant; the
key matching elem is removed from the map. If collection is a vector then
elem is removed as specified by scope, which may be one of:
 $first (removes the first occurrence of elem)
 $last (removes the last occurrence of elem)
 $at (removes the element whose position is denoted by elem)
 $all (removes all occurrences of elem)
sys.remove(["one", "two", "one"], "one", $all)
Example:
returns: ["two"]
sys.remove([1 => "one", 2 => "two"], 1)
returns: [2 => "two"]
boolean sys.member (vague value, vague collection)
Summary: Returns true if value is a member of collection. The latter may be a list,
vector, or map. A value is a member of a list or vector if the latter has an
element of equal value. A value is a member of a map if the map has a key
of equal value.
sys.member("two", ["one", "two", "three"])
Example:
returns: true
sys.member(2, [1=>"one", 2=>"two", 3=>"three"])
returns: true
vector sys.mapKeys (map m, vector keys = null)
Summary: Returns the keys of map m as a vector. When keys is non-null, this vector is
updated and returned. Otherwise, a new vector is created and returned.
sys.mapKeys([1=>"one", 2=>"two", 3=>"three"])
Example:
returns: [1, 2, 3]
Core Reference
417
vector sys.mapValues (map m, vector values = null)
Summary: Returns the values of map m as a vector. When values is non-null, this
vector is updated and returned. Otherwise, a new vector is created and
returned.
sys.mapValues([1=>"one", 2=>"two", 3=>"three"])
Example:
returns: ["one", "two", "three"]
vector sys.sort (vector vec, boolean ascending = true, vector keys = null)
Summary: Sorts and returns the vector denoted by vec. The vector elements should be
either all atomic or all maps or objects. Sorting is done in ascending order,
unless you pass false for ascending. If the elements are maps or object then
pass a vector of the keys to be used for sorting. Each key must be a symbol,
or a list of one symbol; the latter causes that key to be sorted in the reverse
order.
vector v = [
Example:
[$name=>"Peter", $age=>20, $sex=>$male],
[$name=>"Linda", $age=>14, $sex=>$female],
[$name=>"Linda", $age=>23, $sex=>$female]
];
sys.sort(v, true, [$name, ($age)])
returns:
[[$age => 23, $name => "Linda", $sex => $female],
[$age => 14, $name => "Linda", $sex => $female],
[$age => 20, $name => "Peter", $sex => $male]]
int sys.search (vector vec, vague item, boolean ascending = true, symbol key = null)
Summary: Searches the vector denoted by vec, looking for item. The vector must
already be sorted according to the order denoted by ascending. The vector
elements should be either all atomic or all maps or objects. If the vector
elements are maps or objects then key should specify the key on which the
vector is sorted (and to be used for searching).
Returns the index of the closest-matching element, or null if no is match
found.
sys.search(v, "Peter", true, $name)
Example:
returns: 2
int sys.find (vector vec, vague item)
Summary: Returns the index of vec element that is equal to item, or null if there is no
match.
sys.find(["one", 10, $name, 23.2], 10)
Example:
returns: 1
void sys.clear (vague collection)
418
JavaGram Agile Development
Summary:
Clears the contents of collection (which may be a map or vector, but has
no effect when given null). If collection is a string then it’s assumed to
denote a partition name, in which case the partition files are cleared from
the local cache, thus releasing the memory used by them in the cache.
Example:
sys.clear(m)
int sys.length (vague value)
Summary: Returns the length of value. The latter may be a collection, string, or class
instance. The length of a class instance is the count of its fields (including
those in its super classes, but excluding all static fields). Returns 0 when
given null.
sys.length("hello")
Example:
returns: 5
10.10.2.10 Miscellaneous
symbol sys.mode ()
Summary: Returns the runtime mode of the current JavaGram process. This is one of:
$default, $gui, $server, $ide.
sys.mode()
Example:
string sys.clipboard (string data = null)
Summary: When the parameter is null, it returns the contents of the system clipboard,
or null if there is no data on the clipboard. Otherwise, it sets the system
clipboard contents to the string denoted by data, and returns null.
sys.exit(1)
Example:
native sys.env ()
Summary: Returns the internal environment of the current JavaGram thread as a native
object. This can be used in subsequent calls to the sys.java() method.
sys.env()
Example:
void sys.exit (int status = 0)
Summary: Causes the JavaGram process to terminate with the specified status code.
sys.exit(1)
Example:
Exception sys.getAsyncErr ()
Summary: Returns the exception raised by the last asynchronous call, or null. Use this
in your async callbacks to get hold of the exception.
sys.getAsyncErr()
Example:
Core Reference
419
void sys.email (string mailhost, int port, string from, string to, string cc, string bcc,
string subject, string message, vector<string> attach = null,
string contentType = null, string charSet = null)
Summary: Sends an email message with the specified data, using the SMTP server
denoted by mailhost and port. To include attachments, pass a vector of file
paths for attach. For other than text/plain content, pass a value for
contentType (e.g., "text/html"). For other than us-ascii character set, pass
a value for charSet (e.g., "UTF-8").
sys.email("smtp.acme.com", 25, "[email protected]", "[email protected]",
Example:
"", "", "My Subject", "My Message...")
vector<list> sys.fields (object obj, boolean incBases = true, boolean incStatics = false)
Summary: Returns a vector of all the fields for obj, each of which is a list of the form:
($fieldName, "fieldType", fieldValue). When incBase is true, all base
class fields are also included. When incStatics is true, all static fields are
also included.
sys.fields(person)
Example:
vague sys.get (vague data, symbol key)
Summary: Returns the value in data (which may be an object, map, or GUI
component) as denoted by the field, key, or property key. Any object field
can be accessed by this method, regardless of its access qualifier. When
data is a map and key doesn’t exist, null is returned. When data is an
object and key doesn’t exist, void is returned.
sys.get(person, $dob)
Example:
void sys.set (vague data, symbol key, vague value)
Summary: Sets the value in data (which may be an object, map, or GUI component) as
denoted by the field, key, or property key, to value. Any object field can be
accessed by this method, regardless of its access qualifier.
sys.set(person, $dob, [#1992-10-22])
Example:
boolean sys.getable (vague data, symbol key)
Summary: Returns true if data (which may be an object, map, or GUI component) has
the field, key, or property denoted key. For an object, this is the case when
key is actually defined. For a GUI component, this return true if the GUI
element has the property, even if it’s write-only.
sys.getable(person, $dob)
Example:
boolean sys.setable (vague data, symbol key)
420
JavaGram Agile Development
Summary:
Returns true if data (which may be an object, map, or GUI component) has
the field, key, or property denoted key. For an object, this is the case when
key is actually defined and not final. For a map this returns true if the key
type matches that expected by the map. For a GUI component, this return
true if the GUI element has the property, even if it’s read-only.
Example:
sys.setable(person, $dob)
void sys.assign (object left, object right, vague scopeClass)
Summary: Assigns the fields of right to the fields of left, according to the non-static
fields of scopeClass, but excluding the base classes fields of scopeClass (if
any). Both left and right must be of type scopeClass or derived from it.
sys.assign(customer, person, Person)
Example:
vague sys.call ([stream s,] vague objOrClass, string method, ...)
Summary: Dynamically calls a class method. An optional first argument can be used
to specify a server stream, to which a remote call is routed (may be null).
The next argument must denote either a class object (if the method is nonstatic) or a class (class name or symbol). The next argument must specify a
method name (to call a constructor, use "new").
Subsequent arguments are intended for the method. If you want to pass all
the arguments as a single list, add () to the end of the method name, e.g.,
"foo()" instead of "foo".
The call is fully type checked and if it resolves unambiguously to a valid
method then it’s performed. The return value is whatever the method
returns. Any class method can be accessed by this method, regardless of its
access qualifier.
sys.call($Test, "foo", 10, "hello")
Example:
sys.call($Test, "foo()", $(10, "hello"))
sys.call($Point, "new", 100, 120) // Constructor call
boolean sys.callable ([stream s,] vague objOrClass, string method, ...)
Summary: Similar to sys.call(), except that it just checks that a matching method
exists. Returns true if the method can be called successfully.
sys.callable($Test, "foo", 10, "hello")
Example:
vague sys.java (vague obj, string retType, string member, vague args = null)
Core Reference
421
Summary:
Causes a Java member (within JAG or a class accessible via CLASSPATH)
to be accessed. The following rules apply:
 When args is $field, member is treated as a field. Otherwise, it’s treated
as a method.
 When obj is a string, it is treated as a class path, and the method is
assumed to be static (or a constructor). Otherwise, the method is treated
as a method of obj (which should be a Java object returned by an earlier
call to sys.java()).
 When obj is a native value (e.g., GUI component), the underlying Java
value is used. For example, for a text field object, the underlying Java
JTextField object is used.
 When retType is "native", the return value will be a Java object
reference. Otherwise, it’s converted to a JavaGram value.
 When method is "new", it’s resolved to a class constructor.
If the Java method has parameters then the type and name of each
parameter must be specified in the args list or vector. Valid parameter
types and corresponding JavaGram argument types are as follows:
Par Type
JavaGram Arg Type
$boolean
$byte
$short
$int
$long
$char
$float
$double
$void
$string
boolean
int
int
int
int
int
real
real
void
string
full-class-name Java object returned by sys.java()
In a Flash client, sys.java() is emulated. It works for classes, fields, and
methods that are present in the Flash JavaGram runtime.
Example:
sys.java("jag.misc.JagTimer", "native", "new", $($string, "abc"))
void sys.hook (symbol task, vague klass, string method)
422
JavaGram Agile Development
Summary:
This method is for use in JavaGram clients. It provides a callback hook for
task. The callback is the method denoted by method in the class denoted by
klass. Either the class must be singleton or the method static. The method
must take one argument of type vague and return void. Valid values for task
are:
 $ready. Callback is called when the last task has been completed. The
argument is always null.
 $download. Callback is called when a client sends a file download
request to a server. The relative file path is passed as the argument.
 $upload. Callback is called when a client sends a file upload request to a
server. The relative file path is passed as the argument.
 $load. Callback is called when a client starts loading a script. The
relative script path is passed as the argument.
 $remote. Callback is called when a client starts executing a remote
method. The method name is passed as the argument.
Example:
singleton Class MyApp extends GuiApp {
public void onLoadTask (vague script) {
//...
}
public static void main () {
sys.hook($load, MyApp, $onLoadTask);
}
}
Core Reference
423
11
GUI Reference
This chapter specifies the Graphical User Interface (GUI) features of JavaGram. Use this
chapter to look up GUI topics once you’ve become familiar with the language. For a
tutorial style introduction to JavaGram’s GUI capabilities please refer to Chapter 4.
With a couple of exceptions, all GUI elements and methods described in this chapter are
also available in the Flash JavaGram runtime. Where an element or method is not
available, it’s marked with the icon .
11.1 Markup
JavaGram offers a completely different style of GUI programming to Java’s Swing.
Whereas GUI programming in Swing is procedural, JavaGram allows you to define a
GUI declaratively. This has a number of advantages: you write a lot less code, the code is
much more readable, and the code readily portrays the hierarchical structure of the GUI.
As a result, creating sophisticated GUIs in JavaGram is much easier than in Java.
GUI members are defined using a markup notation (similar to text members).
Semantically, however, GUI members behave like class fields.
Here is a simple example:
<jag>
class Sample {
final
<App app lookAndFeel=$windows>
<Frame title="Sample" width=200 height=100 event=frameHandler>
<Panel>
<Layout.border/>
<Field.text value="Sample field data" lay=$north/>
<Button title="OK" lay=$south action={sys.exit()}/>
</Panel>
</Frame>
</App>
protected void frameHandler (native comp, symbol event) {
sys.exit();
}
public static void main () {
Sample s = new Sample();
gui.run(s.app);
}
}
</jag>
Because this is a GUI program, it must be run using javaw.exe and the jag.gui.Gui Java
class rather than java.exe and jag.run.Env. When run, it will display the following frame.
424
JavaGram Agile Development
The <App> element specifies an application. Every GUI element can have an identifier
(app in this case), which behaves like a class field. The identifier is mandatory for toplevel elements (such as <App>) and optional for inner-elements (such as <Frame> in the
above example). Each element also accepts a set of properties. All properties are typed
and must receive a value of their nominated type. For example, the lookAndFeel property
expects a symbol value.
Unlike fields, a final GUI element means that its properties are final and therefore may
not be assigned to.
Within <App>, we have a <Frame> element, which will display an application main frame.
The event property for the frame refers to a class method that will process events
generated by the frame.
Within the frame, we have a <Panel> element which is specified to have a border layout.
Within the panel, we have a <Field.text> element that’s laid north, and a <Button>
element that’s laid south. The action property for the button is defined to exit the
application when the button is pressed.
Note how we run the application: we create an instance of the class and call gui.run() on
the app element. There is another way of doing the same, explained later in this chapter.
11.1.1 Element Qualifiers
A GUI element may be specified with any of the qualifiers allowed for fields, except for
getable and settable.
The delayed qualifier is particularly useful in situations where an element property value
is specified as an expression that refers to class fields that are initialized by a constructor.
This dependency is honored by delaying the creation of the element until the constructor
has done its job.
11.1.2 Element Identifiers
Within each element markup and before the element properties, you can specify an
element identifier. This must be unique within the class scope, and is mandatory for toplevel elements, but optional for child elements. Once defined, you can refer to this
identifier (as if it were a class field) to uniquely identify the GUI element. For example,
the GUI element
<Field.text name value="John Smith"/>
GUI Reference
425
is uniquely identified by the name identifier.
If you have a programmatic reference to an element then you can use it to access the
element identifier. For example, given a variable nameField that denotes the above
element, nameField.id will give $name. This is a read-only value, so you can’t assign to
nameField.id.
11.1.3 Element Properties
For each GUI element, you can specify a set of properties to customize its appearance or
behavior. Each property is specified in the usual markup syntax:
PropertyName = PropertyValue
For example, the button
<Button title="OK" lay=$south action={sys.exit()}/>
has three properties: title specifies the title appearing on the button, lay specifies how
the button is to be laid out in its parent, and action specifies the expression to be
evaluated when the button is pressed.
The documentation of each element type lists the permissible set of properties for the
element. Where an element inherits from another element type, the properties of the latter
are also supported by the former.
All properties are typed. Therefore, the type of the value you specify for a property must
match the type expected by the property. In most cases, a property value is a literal, but it
doesn’t have to be. You can also use expressions, in which case you must enclose the
whole expression in a pair of braces. Most properties are evaluated when the element is
created. Some properties, however, have delayed evaluation. The action property, for
example, is not evaluated when the button is created, but when the button is pressed by
the user.
11.1.3.1
Event Handlers
Some elements allow you to define an event handler property. The syntax for this is:
event = EventMethodName
The event method must be defined in the same class (or in a super class) and have the
following signature:
void eventHandler (native comp, symbol event) {
//…
}
426
JavaGram Agile Development
When the element raises an event, this method is automatically called. The comp
parameter is set to the element raising the event, and the event parameter is set to a
symbol that represents the event. It’s perfectly valid for multiple elements (of the same
type) to share the same event handler. In this case, you can use the comp parameter to find
out which one has raised the event.
11.1.3.2
Data Models
Some elements (such as lists, combos, tables) display composite data. For example, a
combo may contain a list of names to choose from. JavaGram provides two ways of
specifying this data: directly or via a data model.
To use the direct approach, simply set the element’s data property to a vector that
contains the values. For example:
<Combo color data=["Red", "Green", "Blue"]/>
or
<Combo color data={getColors()}/>
The direct approach is easier but less flexible because the entire data set must be
available in advance. Also, if the data set changes, you’ll have to re-assign it to data for it
to take effect.
Using a data model overcomes these limitations. To use a data model, set the element’s
model property to the name of a method in the same class that implements the data model.
For example:
<Combo color model=colorModel/>
The signature of the data model method is dictated by the element type. For example, for
a combo, the syntax is:
vague colorModel (native combo, symbol cmd, int idx) {
//...
}
The combo parameter is set to the specific combo for which the model is invoked, thus
allowing you to share the same model across multiple elements of the same type. The cmd
parameter is set to a model command, which depends on the element type. For a combo,
for example, cmd may be $count (to return the number of entries in the combo) or $get (to
return a specific entry). The idx parameter is set to the zero-based index of an entry when
cmd is $get. The method must return a value in response to cmd.
When an application needs to display the contents of a combo, the framework calls the
data model (possibly a number of times) to get the actual items to display. Therefore, the
data can be easily dynamic (e.g., it can be sourced from a database or a server).
GUI Reference
427
11.1.4 Boiler Plates
If many elements in a class share the same properties then you can eliminate the
repetition and simplify your code by using a boiler plate. For example,
<Plate Req fgColor="0xFF0000" tooltip="Required"/>
defines a boiler plate named Req that has two properties. Here is an example of using it in
an element:
<Req:Label title="Surname"/>
Semantically, this is equivalent to writing:
<Label fgColor="0xFF0000" tooltip="Required" title="Surname"/>
One limitation of boiler plates is that they only be used to capture properties of a <Comp>
element; the latter being the base class of most elements.
11.2 Element Types
Elements specified as <Elem> are containers – they can contain other elements, like this:
<Menu>
<MenuItem .../>
</Menu>
Elements specified as <Elem/> are non-containers.
Where an element is specified to extend another element, this implies that the former
inherits the properties of the latter.
11.2.1 Abstract Elements
Abstract elements capture common behaviors shared by a number of element types. They
can’t be directly instantiated. All abstract elements are shown in italics.
<Comp/>
SUMMARY:
PROPERTY
Most GUI elements inherit from this element, directly or indirectly.
symbol key
vague value
vague handy
The key for binding this element to data (e.g., $age).
The actual value to which this element is bound (e.g., 21).
An arbitrary value associated with the component (defaults to null).
This property is also available for elements that are not derived from
<Comp>.
The type of the value that this element can be bound to (e.g., int).
Type type
428
DESCRIPTION
JavaGram Agile Development
symbol lay
native font
string fgColor
string bgColor
int x
int y
int width
int height
Comp center
string tooltip
boolean focus
symbol cursor
symbol border
vague enable
vague visible
Id event
boolean valid
boolean refresh
native parent
EVENTS:
How the element is laid in its parent container. For a <Panel> or
<Dialog> container, permissible values are: $center, $north, $south,
$east, $west. For a <Pane.split> container, permissible values are:
$north, $south, $east, $west.
A <Font> for displaying any text in the element.
The foreground color as a hexadecimal string representation of an
RGB color (e.g., "0xFF0000" or "#FF0000" for red). If there are more
than 6 hex digits, the left most digits represent an alpha value. For
example, "0x44FF0000" represents a red color with an alpha value of
0x44, making it fairly transparent.
The background color (follows the same format as fgColor).
The left coordinate of the element (in pixels).
The top coordinate of the element (in pixels).
The width of the element (in pixels).
The height of the element (in pixels).
Centers this element relative to the element this property is assigned
to.
The tooltip for the element.
When true, the element has the input focus.
The cursor for the element; must be one of $default, $wait, or $hand.
The physical border of the element; must be one of: $empty, $line,
$bevel, $etched, or $round (default value depends on the current look
and feel).
An expression which is evaluated when gui.maintain() is explicitly
or implicitly invoked. Enables the element when the expression
evaluates to true, and disables it when it evaluates to false. Defaults
to true.
An expression which is evaluated when gui.maintain() is explicitly
or implicitly invoked. Shows the element when the expression
evaluates to true, and hides it when it evaluates to false. Defaults to
true.
The event handler for this element, which must be a method in the
same class. The events raised by the element will depend on the
element type.
Returns true if component is valid (a component is valid when it is
correctly sized and positioned within its parent container and all its
children are also valid). Set to false to force the component to relayout. Setting it to true has no effect.
Refreshes the element by repainting it.
The direct parent of this component, or null.
 $bind is raised after the element is bound to data.
 $save is raised before changes to element data are saved.
<Container> extends <Comp>
GUI Reference
429
SUMMARY:
GUI elements that are containers inherit from this element, which
implements containment functionality.
In some situations, you may want to dynamically add/remove
elements to/from a container. You can do this, using the += and -=
operators.
PROPERTY
DESCRIPTION
boolean report
Indicates whether this container should take part in report generation
(see gui.toHtml()). Defaults to true.
When set to true, it enables the container’s binding (as denoted by its
key) to be a vector. The container is bound to a vector’s element
whose index is denoted by the zero-based index of the container
relative to its parent container.
boolean indexed
EXAMPLE:
node1 += node2
node1 -= node3
// add node2 as a child of node1
// remove child node3 from node1
<Layout/>
SUMMARY:
Creates a layout for a panel. A layout must appear as the first element
inside the panel.
<AbsButton/> extends <Comp>
SUMMARY:
All button elements inherit from this element.
PROPERTY
DESCRIPTION
string title
vague image
The title displayed inside the button.
Optional image to be displayed to the left of the button title. This
may be either an <Icon> or the path of a GIF file.
The alignment of button contents; must be one of $center, $north,
$south, $east, $west, $northEast, $northWest, $southEast, or
$southWest (defaults to $center).
The alignment of button text relative to image; must be one of
$center, $north, $south, $east, $west, $northEast, $northWest,
$southEast, or $southWest (defaults to $east).
The horizontal inner-margin for the left and right of the button.
The vertical inner-margin for the top and bottom of the button.
When true, the button is selected (pushed, ticked, etc.). Defaults to
false.
An arbitrary expression (action handler) which is evaluated when the
button is pressed. Push buttons don’t have event handlers; use the
action property instead. For other buttons, please note that if you
define the action property as well the event property, only the most
recently-set of these two will handle an activation of the button. For
clarity, it’s advisable to use only one of these two properties.
 $select is raised when a check box or radio button is selected or
deselected.
symbol align
symbol textPos
int hGap
int vGap
boolean select
vague action
EVENTS:
<AbsText/> extends <Comp>
430
JavaGram Agile Development
SUMMARY:
PROPERTY
boolean readOnly
boolean lineWrap
string insert
boolean ime
EVENTS:
DESCRIPTION
When true, the text can’t be edited (defaults to false).
When true, lines wrap around (defaults to false).
Setting this property to a string causes the string to be inserted at
the current caret position. You cannot get this property.
Useful for fields that require Chinese, Korean, or Japanese input, as
activated by the IME setting of the machine. Setting this property
to true causes a text dialog to be displayed that’s capable of
receiving keystrokes in the aforementioned languages. When the
user OK’s the dialog, the field is updated accordingly and an $ime
event is raised. You cannot get this property.
 $key is raised when a keyboard input occurs.
 $active is raised when the field is activated (e.g., by clicking in
it).
 $focus is raised when the field loses focus.
 $ime is raised when the text is changed via the IME text dialog.
<AbsNumeric/> extends <Comp>
SUMMARY:
All numeric fields inherit from this element. Data within the field is
formatted and non-numeric data is not accepted. Pressing the right
mouse button on the field brings up a pop-up menu of three options:
 Calculator (enabled if the field is not a date or time field).
Displays a visual calculator for calculating a value for the field.
 Calendar (enabled if the field is a date field). Displays a visual
calendar for choosing a date for the field.
 Clock (enabled if the field is a time field). Displays a visual clock
for choosing a time for the field.
 Clear (always enabled). Clears the contents of the field.
As a shortcut, double-clicking an editable numeric field brings up
the calculator or the calendar, depending on the field type (see
derived field types).
PROPERTY
DESCRIPTION
boolean readOnly
When true, the field is read-only.
GUI Reference
431
symbol align
string format
boolean auto
EVENTS:
The alignment of data within the field; must be one of $center,
$east, or $west (defaults to $west).
The format of the data within the field, having a similar effect to
sys.format().
When set to true, a single left-click in the field also causes the
field’s corresponding edit dialog to open (defaults to false).
 $active is raised when the field is activated (e.g., by clicking in
it).
 $modified is raised when the field value is modified.
 $focus is raised when the field loses focus.
<AbsGraph/> extends <Comp>
SUMMARY:
All graph elements inherit from this element.
PROPERTY
DESCRIPTION
string title
boolean legend
symbol legendLay
The graph title.
When true, a legend is displayed for the graph (defaults to true).
Specifies where the graph legend should appear; must be one of:
$south, $southWide or $east (defaults to $east). $southWide causes
the legend to be drawn as one line, rather than stacked as multiple
lines.
 $select is raised when the user clicks on a graph canvas.
 $drill is raised when the user double-clicks on a graph canvas.
EVENTS:
11.2.2 Application Elements
<App>
SUMMARY:
Represents a Swing application. The only element allowed inside
an <App> is a <Frame>, which represents the application’s main
frame.
PROPERTY
DESCRIPTION
string title
symbol lookAndFeel
native stdOut
native stdErr
The application title.
The application look and feel; must be one of $default, $windows,
$motif, or $metal (default is platform dependent).
When true, the application frame is displayed (defaults to true).
When true, the application frame is centered on the screen
(defaults to false).
An <Area.text> for redirecting standard output to.
An <Area.text> for redirecting standard error to.
EXAMPLE:
<App myApp title="My App" lookAndFeel=$windows/>
boolean visible
boolean center
<Font/>
SUMMARY:
PROPERTY
Creates a font with the specified properties.
string name
The font name (defaults to "System").
432
DESCRIPTION
JavaGram Agile Development
Symbol style
int size
boolean embed
The font style; must be one of: $plain, $bold, $italic, $boldItalic
(defaults to $plain).
The font size (defaults to 10).
Used by Flash clients (has no effect in native clients). If you use an
embedded font (see gui.loadFont()), you must set this to true
(defaults to false).
EXAMPLE:
<Font codeFont name="Courier" size=12/>
11.2.3 Window Elements
<Frame> extends <Container>
SUMMARY:
Creates an application main frame. You typically add a <MenuBar>
and a <Panel> to a frame, and use the latter as the container for
whatever needs to be displayed in the frame. You can also add a
<Layout> to a frame to define the frame content layout.
PROPERTY
DESCRIPTION
boolean sizeable
string title
vague image
When true, the frame can be resized by the user (defaults to true).
The title displayed at the top of the frame.
Optional icon image to be displayed next to the title. This may be
an <Icon> reference or the path of an image GIF file.
Defines the default button for the frame. This button is pressed
when the user hits the enter key.
The visual state of the frame, which may be one of: null (normal),
$max (maximized), $min (minimized/iconified).
 $close is raised when the user attempts to close the frame.
Button enter
symbol state
EVENTS:
EXAMPLE:
<Frame myFrame title="Test" width=800 height=600>
<MenuBar>
//...
</MenuBar>
<Panel>
//...
</Panel>
</Frame>
<Dialog> extends <Container>
SUMMARY:
Creates a dialog window.
PROPERTY
DESCRIPTION
native owner
boolean fixed
boolean modal
boolean show
string title
vague image
The dialog owner; this is typically set to a <Frame> or another <Dialog>.
When true, the dialog has a fixed size (defaults to false).
When true, the dialog is modal (defaults to false).
When true, the dialog is visible (defaults to false).
The title displayed at the top of the dialog (defaults to "").
Optional icon image to be displayed next to the title. This may be
an <Icon> reference or the path of an image GIF file.
Defines the default button for the dialog. This button is pressed when
the user hits the enter key.
Button enter
GUI Reference
433
EVENTS:
EXAMPLE:
 $close is raised when the user attempts to close the dialog.
<Dialog d owner={myFrame} width=400 height=300 enter={okButn}>
<Layout.border/>
//...
<Button okButn lay=$south title="OK" action={d.show=false}/>
</Dialog>
<Alert/>
SUMMARY:
PROPERTY
Displays an alert dialog window for providing feedback to the user.
string
string
native
symbol
The title displayed at the top of the alert box.
The message displayed inside the alert box.
The alert owner; this is typically set to a <Frame> or a <Dialog>.
This may be set to one of:
 $error (for displaying an error message)
 $info (for displaying feedback information)
 $warning (for displaying a warning message)
 $question (for asking a question)
 $plain (for any other purpose)
A vector of up to three strings to be used as button labels in the alert
box.
The zero-based index of the default button (pressed when the user
hits the enter key).
The result handler for this element, which must be a method in the
same class, having one int parameter. When the alert is dismissed,
this method is called with the zero-based index of the pressed button
(or null if the alert has just one button or no button is pressed) as
argument. In a synchronous GUI, you don’t need such a handler –
use the value returned by the show property instead.
You can’t set this property. When you get this property, the alert is
displayed and the zero-based index of the button the user presses is
returned. When the alert has just one button or when it’s closed
without pressing a button, null is returned. In a Flash client, always
null is returned.
title
message
owner
kind
vector options
int enter
Id result
int show
EXAMPLE:
DESCRIPTION
<Alert alert owner={myFrame} title="Sample"
message="Alert Message" kind=$warning
options=["One", "Two", "Three"] enter=1/>
<Alert.ync/> extends <Alert>
SUMMARY:
Same as <Alert> except that the kind property is set to $question and
the options property is set to ["Yes", "No", "Cancel"].
EXAMPLE:
<Alert.ync alert owner={myFrame} title="Sample" message="Alert
Message" enter=1/>
<FileChooser/>
434
JavaGram Agile Development
SUMMARY:
Displays a File Chooser dialog window for choosing one or
more files or directories from the file system.
PROPERTY
DESCRIPTION
boolean multiSelect
When true, allows the user to select multiple items (defaults to
false).
When true, allows the user to select a file (defaults to true).
When true, allows the user to select a directory (defaults to
false).
The title appearing at the top of the dialog.
The dialog owner; this is typically set to a <Frame> or a
<Dialog>.
This may be set to $open (for opening a file) or $save (for saving
a file). Defaults to $open.
The initial path for the File Chooser (defaults to the current
directory).
The file extension(s) to act as filter (defaults to none). This may
be a string (e.g., "doc") or a vector of strings.
The description for the file filter (defaults to "Filtered Files").
The custom label appearing on the dialog’s main button.
The preferred file path format. May be set to $unix or $dos
(defaults to $unix).
The result handler for this element, which must be a method in
the same class, having one vague parameter. When the dialog is
closed by pressing the OK button, this method is called with the
selection result as argument. In a synchronous GUI, you don’t
need such a handler – use the value returned by the show
property instead.
You can’t set this property. When you get this property, the
dialog is displayed and the absolute path of the file or directory
selected is returned. When multiSelect is true, the result is a
vector of paths. If nothing is selected, null is returned. In a
Flash client, always null is returned.
boolean file
boolean dir
string title
native owner
symbol kind
string path
vague ext
string desc
string label
symbol format
Id result
vague show
EXAMPLE:
<FileChooser fc owner={myFrame} kind=$open
title="Choose file(s)" path="C:/JavaGram/"
ext=["doc","zip"] multiSelect=true
desc="DOC and ZIP files" label="Do it"/>
<ColorChooser/>
SUMMARY:
Displays a Color Chooser dialog window for selecting a color from a
color menu.
PROPERTY
DESCRIPTION
string title
native owner
string color
The title appearing at the top of the dialog.
The dialog owner; this is typically set to a <Frame> or a <Dialog>.
The selected color as a hexadecimal string (e.g., "0x00FF00"). You
can also set the initial color selection by assigning to this property.
GUI Reference
435
Id result
The result handler for this element, which must be a method in the
same class, having one string parameter. When the dialog is closed
by pressing the OK button, this method is called with the selected
color as argument. In a synchronous GUI, you don’t need such a
handler – use the value returned by the show property instead.
string show
You can’t set this property. When you get this property, the dialog is
displayed and the selected color is returned. If no color is selected,
null is returned. In a Flash client, always null is returned.
EXAMPLE:
<ColorChooser cc owner={myFrame}
title="Choose your color" color="0xAABBFF"/>
11.2.4 Layout Elements
<Layout.flow/> extends <Layout>
SUMMARY:
Creates a flow layout for its <Panel> parent. Panel children are arranged
left to right and top to bottom. This is the default layout for a panel with
unspecified layout.
PROPERTY
DESCRIPTION
int hGap
int vGap
symbol align
The horizontal gap for the left and right of the layout (defaults to 0).
The vertical gap for the top and bottom of the layout (defaults to 0).
Specifies how elements are to be aligned in the panel, and may be one
of $east, $center, or $west.
EXAMPLE:
<Layout.flow align=$west/>
<Layout.border/> extends <Layout>
SUMMARY:
Creates a border layout for its <Panel> parent. Panel children are
arranged north, south, center, east, or west according to the lay property
of each element.
PROPERTY
DESCRIPTION
int hGap
int vGap
The horizontal gap for the left and right of the layout (defaults to 0).
The vertical gap for the top and bottom of the layout (defaults to 0).
EXAMPLE:
<Layout.border hGap=2 vGap=2/>
<Layout.card/> extends <Layout>
SUMMARY:
Creates a card layout for its <Panel> parent. Panel children (which must
be containers, each having a unique identifier) are stacked like a deck of
card so that only one can be visible at a time. Initially, the first card is
visible, but the visible card can be changed by setting the top property.
PROPERTY
DESCRIPTION
int hGap
int vGap
native top
The horizontal gap for the left and right of the layout (defaults to 0).
The vertical gap for the top and bottom of the layout (defaults to 0).
Denotes the currently visible child container.
EXAMPLE:
<Layout.card/>
<Layout.grid/> extends <Layout>
436
JavaGram Agile Development
SUMMARY:
Creates a grid layout for its <Panel> parent. Panel children are arranged
left to right and top to bottom in a fixed grid of a given number of rows
and columns.
PROPERTY
DESCRIPTION
int
int
int
int
The horizontal gap for the left and right of the layout (defaults to 0).
The vertical gap for the top and bottom of the layout (defaults to 0).
The number of rows for the grid (defaults to 1).
The number of columns for the grid (defaults to 1).
hGap
vGap
rows
cols
EXAMPLE:
<Layout.grid rows=2 cols=3/>
<Layout.gridBag/> extends <Layout>
SUMMARY:
Creates a grid bag layout for its <Panel> parent. Panel children are
arranged within the grid bag using the <Lay> markup.
EXAMPLE:
<Layout.gridBag/>
<Layout.horizontal/> extends <Layout>
SUMMARY:
Creates a horizontal layout for its <Panel> parent. Panel children are laid
out along a horizontal axis.
PROPERTY
DESCRIPTION
int gap
The horizontal gap between the components (defaults to 0).
EXAMPLE:
<Layout.horizontal/>
<Layout.vertical/> extends <Layout>
SUMMARY:
Creates a vertical layout for its <Panel> parent. Panel children are laid out
along a vertical axis.
PROPERTY
DESCRIPTION
int gap
The vertical gap between the components (defaults to 0).
EXAMPLE:
<Layout.vertical/>
<Lay>
SUMMARY:
This is used for laying out a child element inside a panel that has grid
bag layout. It represents a cell of the grid bag.
PROPERTY
DESCRIPTION
int row
int col
int rowSpan
int colSpan
symbol align
The zero-based row of the cell in the grid bag.
The zero-based column of the cell in the grid bag.
The number of rows covered by the cell (defaults to 1).
The number of columns covered by the cell (defaults to 1).
The alignment of an element within the cell. It may be one of $north,
$south, $center, $east, $west, $northEast, $northWest, $southEast, or
$southWest (defaults to $center).
May be one of the following:
 $none (this is the default).
 $horizontal (the element fills the horizontal space of the cell).
 $vertical (the element fills the vertical space of the cell).
 $both (the element fills the horizontal and vertical space of the cell).
symbol fill
GUI Reference
437
int margin
real weight
The margin on the four sides of the cell (defaults to 0).
The weight of the cell in relation to other cells (defaults to 1.0). When
the panel is resized, this value is used to determine how the cell should
be resized in relation to other cells. Use 0.0 for very tight packing, or a
value greater than 1.0 for generous packing.
EXAMPLE:
<Lay row=0 col=0 weight=0.1 margin=4 align=$east>
<Label title="Name"/>
</Lay>
<Filler/> extends <Comp>
SUMMARY:
Creates an invisible element to be used for filling space when laying out
elements in a panel. Use the width and/or height properties of <Comp> to
specify the physical size of the filler.
EXAMPLE:
<Filler width=20>
11.2.5 Container Elements
<Panel> extends <Container>
SUMMARY:
Creates a panel. A panel is the most commonly-used container because
it can layout various elements in a number of different ways. Unless
you specify a layout for a panel, it assumes a flow layout. If specified, a
layout must appear as the first element within the panel. A panel is
drawn without a visible border, unless it has a title or an explicit
border.
PROPERTY
DESCRIPTION
string title
The title displayed at the top-left of the panel. A panel that has a nonempty title is drawn with a title bar.
The link displayed at the top-right of the panel, provided the panel also
has a non-empty title.
 $add event is raised when an element is added to the panel.
 $remove event is raised when an element is removed from the panel.
 $resize event is raised when the panel is resized.
 $link event is raised when the panel has a link and it’s clicked by the
user.
string link
EVENTS:
EXAMPLE:
<Panel title="Client Details">
//...
</Panel>
<Panel.toggle> extends <Panel>
SUMMARY:
Creates a panel for containing a group of radio buttons or toggle
buttons. Initially, none of the elements is selected. When an element is
selected (by the user or programmatically) the previously selected
element (if any) is automatically deselected.
PROPERTY
438
DESCRIPTION
JavaGram Agile Development
vague select
The zero-based index or the title of the selected element.
The type of this property is dependent on the type property of the
toggle panel. If the type is unspecified or is specified to be integer, then
select is of int type. Otherwise, it is of string type. This is the same
as the value property.
EXAMPLE:
<Panel.toggle options>
//...
</Panel>
<Pane.scroll> extends <Container>
SUMMARY:
Provides scrolling capability for its single child element. The
horizontal and vertical scrollbars are displayed on demand (i.e.,
when there is insufficient space in the scroll pane to display the
child.
PROPERTY
DESCRIPTION
boolean horizontal
When false, the horizontal scrollbar is never shown (defaults to
true).
When false, the vertical scrollbar is never shown (defaults to
true).
The current value (thumbnail position) of the horizontal scrollbar.
This is a number in the range hMin to hMax.
The current value (thumbnail position) of the vertical scrollbar.
This is a number in the range vMin to vMax.
The minimum value of the horizontal scrollbar (defaults to 0).
The maximum value of the horizontal scrollbar.
The minimum value of the vertical scrollbar (defaults to 0).
The maximum value of the vertical scrollbar.
 $hScroll event is raised when the user scrolls horizontally.
 $vScroll event is raised when the user scrolls vertically.
boolean vertical
int hValue
int vValue
int
int
int
int
hMin
hMax
vMin
vMax
EVENTS:
EXAMPLE:
<Pane.scroll>
//...
</Pane>
<Pane.split> extends <Container>
SUMMARY:
Creates a split pane, which must contain two child elements. The
pane is split into a top and bottom or left and right parts, the
divider in between which can be adjusted by the user. The lay
property of the child elements may be set to one of $north,
$south, $east, or $west.
PROPERTY
DESCRIPTION
vague divider
When an integer, it denotes the location of the divider. When a
real, it denotes the relative location of the divider as a value in the
range 0.0 to 1.0. The latter is effective only after the split pane
has been rendered.
A value in the range 0.0 to 1.0 which specifies the relative weight
of the left (or top) part for resizing purposes.
real weight
GUI Reference
439
boolean autoExpand
native
native
native
native
west
east
north
south
EXAMPLE:
When true, two small arrows are displayed on the divider which,
when clicked, cause it to expand/contract (defaults to false).
The west child of the split pane.
The east child of the split pane.
The north child of the split pane.
The south child of the split pane.
<Pane.split weight=0.5 autoExpand=true>
<Panel lay=$west>
//...
</Panel>
<Panel lay=$east>
//...
</Panel>
</Pane>
<Pane.tabbed> extends <Container>
SUMMARY:
Creates a multi-tab pane. Each child element must be a <Tab>.
PROPERTY
DESCRIPTION
int count
int select
native front
symbol align
Returns the number of tabs in the pane. You can’t set this property.
The zero-based index of the currently selected tab (defaults to 0).
The currently-selected tab (defaults to the first tab).
The alignment of the tab buttons, which may be one of $north, $south,
$east, or $west (defaults to $north).
When true, each tab button includes a ‘close’ check box to the right of
its title which, when clicked, removes the tab (defaults to false).
 $select event is raised when a tab is selected.
boolean
closable
EVENTS:
EXAMPLE:
<Pane.tabbed>
<Tab>
//...
</Tab>
//...
</Pane>
<Pane.accordion> extends <Container>
SUMMARY:
Similar to <Pane.tabbed> except that the tabs are shown as an accordion
– a vertically arranged set of buttons where each button represents a tab
and only one is expanded at a time.
PROPERTY
DESCRIPTION
int count
Returns the number of tabs in the accordion. You can’t set this
property.
The zero-based index of the currently selected tab (defaults to 0).
 $select event is raised when a tab is selected.
int select
EVENTS:
440
JavaGram Agile Development
EXAMPLE:
<Pane.accordion>
<Tab>
//...
</Tab>
//...
</Pane>
<Tab> extends <Container>
SUMMARY:
Creates a tab element (to be used as a child of a <Pane.tabbed>).
PROPERTY
string title
vague image
vague action
EVENTS:
EXAMPLE:
DESCRIPTION
The title displayed in the tab button.
Optional image to be displayed to the left of the tab title. This may be
either an <Icon> or the path of a GIF file.
An arbitrary expression (action handler) which is evaluated when the
tab is selected.
 $close event is raised when the tab’s parent is specified to be closable
and the user clicks in the close button of the tab. The tab is not
automatically closed, but you can remove it in response to this event.
<Tab title="Details">
//...
</Tab>
<MenuBar> extends <Container>
SUMMARY:
Creates a menu bar to contain one or more menus.
EXAMPLE:
<MenuBar>
<Menu>
//...
</menu>
//...
</MenuBar>
<Menu> extends <MenuItem>
SUMMARY:
Creates a menu to contain zero or more menu items, or separators.
EXAMPLE:
<Menu>
<MenuItem title="Open..."/>
<Separator/>
//...
</Menu>
<Menu.popup> extends <Comp>
SUMMARY:
Creates a popup menu to contain zero or more menu items, or
separators. The menu is displayed when the right mouse button is
held down in any of the menu’s owners.
PROPERTY
DESCRIPTION
string title
vague owner
The menu title.
The menu owner(s). This may be an element or a vector of elements.
GUI Reference
441
native invoker
You can’t set this property. You can get it to find out the owner
element that has invoked the menu.
EXAMPLE:
<Menu.popup popup title="Poupup" owner={panel}>
<MenuItem title="Open..."/>
<Separator/>
//...
</Menu>
<ToolBar> extends <Container>
SUMMARY:
Creates a toolbar to contain zero or more buttons or separators.
PROPERTY
DESCRIPTION
boolean floatable
symbol orientation
When true, the toolbar can be floated (defaults to true).
The toolbar’s orientation, which may be $horizontal or
$vertical (defaults to $horizontal). This causes the toolbar to
use a horizontal or vertical layout. Alternatively, you can define
an explicit layout for the toolbar. For example, to arrange the
toolbar buttons in a grid, define a <Layout.grid/> for it. As with a
panel, the layout must be the first element within the toolbar’s
definition.
EXAMPLE:
<ToolBar tb floatable=false>
<Button title="Submit"/>
<Separator/>
//...
</ToolBar>
11.2.6 Menu Item Elements
<MenuItem/> extends <AbsButton>
SUMMARY:
Creates a menu item. This must be a child element of a menu.
PROPERTY
DESCRIPTION
string accelerator
string mnemonic
A keyboard shortcut for the menu item. Valid examples are:
"INSERT", "control DELETE", "alt shift X", "typed A".
A virtual key shortcut for the menu item (e.g., "A", "DELETE",
"F1").
EXAMPLE:
<MenuItem title="Open..." accelerator="control O"/>
<MenuItem.tick/> extends <MenuItem>
SUMMARY:
Creates a tick-able menu item. Use the select property to find out if the
item is ticked.
EXAMPLE:
<MenuItem.tick title="Verbose"/>
<MenuItem.radio/> extends <MenuItem>
SUMMARY:
Creates a radio menu item. Use the select property to find out if the item
is selected.
EXAMPLE:
442
<MenuItem.radio title="Tabular"/>
JavaGram Agile Development
11.2.7 Decorative Elements
<Label/> extends <Comp>
SUMMARY:
Creates a label for decorating other elements.
PROPERTY
DESCRIPTION
string title
vague image
The label’s title (defaults to "").
Optional image to be displayed to the left of the label title. This may
be either an <Icon> or the path of a GIF file.
symbol align
The alignment of the label; must be one of $center, $north, $south,
$east, $west, $northEast, $northWest, $southEast, or $southWest
(defaults to $west).
symbol textPos
The alignment of label text relative to image; must be one of $center,
$north, $south, $east, $west, $northEast, $northWest, $southEast, or
$southWest (defaults to $east).
EVENTS:
 $click event is raised when the mouse pointer is clicked on the label.
EXAMPLE:
<Label title="Name" align=$east/>
<Icon/>
SUMMARY:
Creates an image icon, which you can subsequently use to set the image
property of other elements. This is more efficient than using the image
file path, as it causes the icon image to be created once and reused
many times.
PROPERTY
DESCRIPTION
int width
int height
string image
Returns the image width. You can’t set this property.
Return the image height. You can’t set this property.
The full path of the image file. The recommended practice is to use the
sys.use() method to translate the relative image path to its absolute
path, which will also demand-download the image file if necessary.
EXAMPLE:
<Icon icon image={sys.use("gifs/Apply.gif")}/>
<Image/>/> extends <Comp>
SUMMARY:
Creates an image component for displaying an image and optional
markers on the image. When the cursor is placed over a marker it
changes to a ‘hand’ cursor to signify that it’s selectable.
PROPERTY
DESCRIPTION
string image
The full path of the image file. The recommended practice is to use the
sys.use() method to translate the relative image path to its absolute
path, which will also demand-download the image file if necessary.
GUI Reference
443
vector<map>
markers
The image markers, where each marker is specified as a map having the
following keys. (All color values must be specified in hexadecimal
format, e.g., "0x00AA00").

$shape may denote one of $square, $circle, $triangle, or $diamond
(defaults to $circle).

$bounds denotes the boundary rectangle of the marker as a list of the
form: (x, y, width, height).

$title may specify an optional title for the marker as a list of the
form: ("...", fontSize, color, style). To use the default
fontSize, pass null for it. The style must be one of $plain, $bold,
$italic, or $boldItalic. The title is always drawn in the middle of
the marker.

$line may specify an optional outline for the marker as a list of the
form: (weight, color). The weight must be specified as a real
value.

int select
EVENTS:
EXAMPLE:
$fill may specify an optional fill color for the marker as a list of
the form: (transparency, color). The transparency must be
specified as a value in the range 0.0 to 1.0, where 0.0 means totally
transparent (which gets defaulted back to 1.0) and 1.0 means
completely opaque.
Specifies the index of the currently selected marker. Setting this
property to a new value causes the $select event to be raised.
 $select event is raised when the mouse pointer is clicked on the
image or a marker.
<Image myImage image={sys.use("images/Tree.jpg")} Markers=[...]/>
<Separator/> extends <Comp>
SUMMARY:
Creates a separator for use in menus, toolbars, and containers.
symbol orientation
The separator’s orientation, which may be $horizontal or
$vertical (defaults to $horizontal). For menus and toolbars, you
don’t need to define this property, as JavaGram automatically
adjusts the orientation to suit them.
EXAMPLE:
<Panel>
<Layout.border/>
<Separator lay=$north/>
<Area.text lay=$center/>
</Panel>
11.2.8 Field Elements
<Field.text/> extends <AbsText>
SUMMARY:
Creates a free-format text field.
PROPERTY
444
DESCRIPTION
JavaGram Agile Development
int cols
symbol align
The number of logical columns in the field.
The alignment of text within the field, which may be one of $west,
$center, or $east (defaults to $west).
EXAMPLE:
<Field.text category align=$center/>
<Field.password/> extends <Field.text>
SUMMARY:
Creates a password field which, for security, obscures user input.
EXAMPLE:
<Field.password passwd/>
<Field.number/> extends <AbsNumeric>
SUMMARY:
Creates a numeric field. Numeric fields can be edited in two ways:
directly by typing into the field, or via a visual editor by double
clicking the field or clicking on the button to the right of the field. The
visual editor is dependent on the field type – for a number field, a
calculator is displayed; for a date field, a calendar is displayed; for a
time field, a clock is displayed (see <AbsNumeric>).
PROPERTY
DESCRIPTION
real min
int precision
The minimum possible value for the field (not applicable to date
fields). Defaults to 0.0. Setting this property to null will cause to
assume the largest negative real number.
The maximum possible value for the field (not applicable to date
fields). Defaults to the largest real number. Setting this property to null
will cause to assume the largest positive real number.
The default value for the field (not applicable to date fields). Defaults
to 0.0.
The number of decimal places allowed in the field (defaults to 0).
EXAMPLE:
<Field.number temperature min=0 max=1000/>
real max
real def
<Field.percent/> extends <Field.number>
SUMMARY:
Creates a number field for displaying percentage values. Values are
formatted according to the format string "0,000.0%".
EXAMPLE:
<Field.percent rate/>
<Field.currency/> extends <Field.number>
SUMMARY:
Creates a number field for displaying currency values. Values are
formatted according to the format string "$0,000.00".
EXAMPLE:
<Field.currency loan/>
<Field.time/> extends <Field.number>
SUMMARY:
Creates a time field, which displays time values in the format
hh:mm:ss.
EXAMPLE:
<Field.time departure/>
<Field.date/> extends < AbsNumeric>
GUI Reference
445
SUMMARY:
vector limit
Creates a date field, which displays date values in the format yyyy-MMdd. The format property can be used to display date in other formats,
including date-and-time.
Optionally specifies the potential dates that can be accepted by the field.
Each vector element can be either a date or a list of the form (fromDate,
toDate). The field’s popup calendar highlights the allowed dates and
dims the rest so that they can’t be selected.
EXAMPLE:
<Field.date dob/>
11.2.9 Selection Elements
<Combo/> extends <Comp>
SUMMARY:
Creates a combo box, allowing the user to select from a list of
values. If the combo values are numbers (as provided by the data
or model property) and the combo has binding (as denoted by its
key property), set its type property to int or real. This enables the
binding process to use the correct type when saving the combo’s
selected value into an object.
PROPERTY
DESCRIPTION
vector data
Direct data for the combo. The combo uses this to build an internal
data model (defaults to null).
The data model for the combo, which must be a method in the
same class (defaults to null). It must have the following signature:
Id model
vague myModel (native combo, symbol cmd, int idx)
Possible values for cmd are:
 $count (return the number of items in the combo).
 $get (return the item denoted by idx).
int select
string selectVal
boolean readOnly
int minPopupWidth
EVENTS:
The zero-based index of the currently selected row (or null).
The string representation of the currently selected row.
When true, the combo can’t be edited (defaults to false).
Specifies a minimum width for the combo popup (defaults to 0).
 $select is raised when a value is selected from the combo.
EXAMPLE:
<Combo status model=statusModel/>
<List/> extends <Comp>
SUMMARY:
Creates a list box.
PROPERTY
DESCRIPTION
vector data
Direct data for the list. The list uses this to build an internal data
model (defaults to null).
The data model for the list, which must be a method in the same
class (defaults to null). It must have the following signature:
Id model
vague myModel (native comp, symbol cmd, int idx)
Possible values for cmd are:
 $count (return the number of items in the list).
 $get (return the item denoted by idx).
446
JavaGram Agile Development
vague select
EVENTS:
The index of the currently-selected item(s). This is a vector of
integers (may be empty) when multiSelect is true, or an integer
(or null) otherwise. For convenience, you can assign an integer to
this property even when multiSelect is true.
Set this property to true to select all items in the list. Set it to
false, to deselect all items. Getting this property always returns
null.
The currently-selected item(s). This is a vector (may be empty)
when multiSelect is true, or an integer (or null) otherwise.
When true, the user can select multiple values in the list by
holding the shift or control key down and clicking. When false,
only one value can be selected at a time.
 $select is raised when a value is selected from the list.
EXAMPLE:
<List color data=["Red", "Green", "Blue"]/>
boolean selectAll
vague selectVal
boolean mutiSelect
<List.tick/> extends <List>
SUMMARY:
Creates a list box of tickable items.
PROPERTY
DESCRIPTION
vector<list> data
Same as <List> direct data, except that each item should be a list
of two elements: (name, tick), where tick is true of false. You
can also use a list of one item (name) in which case tick defaults
to false.
Same as <List> model, plus the following commands:
 $tick (return true if the item denoted by idx is ticked).
 $toggle (toggle the tick state of the item denoted by idx).
Set this property to true to tick all items in the list. Set it to false,
to untick all items. Getting this property always returns null.
 $select is raised when a value is selected from the list.
 $toggle is raised when one or more items change their tick
state.
Id model
boolean tickAll
EVENTS:
EXAMPLE:
<List.tick remind data=[("Today", true),
("Tomorrow", false), ("Next Week", true)]/>
<Option.tick/> extends <AbsButton>
SUMMARY:
Creates a checkbox.
EXAMPLE:
<Option.tick married title="Married"/>
<Option.radio/> extends <AbsButton>
SUMMARY:
Creates a radio button.
EXAMPLE:
GUI Reference
<Panel.toggle sex>
<Option.radio title="Male"/>
<Option.radio title="Female"/>
</Panel>
447
<Slider/> extends <Comp>
SUMMARY:
Creates a slider.
PROPERTY
DESCRIPTION
map title
The slider’s title map (defaults to [=>]). This may be used to map
slider values to labels on the slider.
The minimum possible value for the slider.
The maximum possible value for the slider.
The slider’s orientation, which may be $horizontal or $vertical.
An arbitrary expression (action handler) which is evaluated when
the slider’s value changes. Sliders don’t have event handlers; use
the action property instead.
int min
int max
symbol orientation
vague action
EXAMPLE:
<Slider risk title=[0=>"min", 1=>"low", 2=>"medium",
3=>"high", 4=>"max"] min=0 max=4 value=2
orientation=$horizontal action={doAction()}/>
<RangeBar/> extends <Comp>
SUMMARY:
Creates a range bar. Like a slider, a range bar has a minimum and
a maximum value, but its thumb denotes two values: start and
finish, both of which fall in between min and max, and start is
always less than finish. The thumb size is proportional to the
‘distance’ between start and finish.
PROPERTY
DESCRIPTION
real min
real max
real start
real finish
symbol align
The minimum value for the range bar (defaults to 0.0).
The maximum value for the range bar (defaults to 100.0).
The start value for the thumb (defaults to 40.0).
The finish value for the thumb (defaults to 60.0).
Determines the bar’s orientation and relative position of labels,
which may be one of: $north (horizontal orientation and labels
displayed above the bar), $south (horizontal orientation and labels
displayed below the bar), $west (vertical orientation and labels
displayed to the left of the bar), or $east (vertical orientation and
labels displayed to the right of the bar). It defaults to $south.
A string representing the format for rendering the labels, or a
method in the same class returning a formatted label. The method
must have the following signature:
vague format
string formatFun(native rangeBar, real val)
This property defaults to null and, if not set, the labels are not
vague action
boolean dynamic
448
displayed.
An arbitrary expression (action handler) which is evaluated when
the thumb is moved or resized. Range bars don’t have event
handlers; use the action property instead.
When true, the action handler is called as the user drags or
resizes the thumb. Otherwise, the action handler is called only
once when the user releases the mouse button. It defaults to false.
JavaGram Agile Development
int pause
Specifies the delay (in milliseconds) between animation frames
(defaults to zero). When set to a non-zero value (e.g., 1000), it
causes a set of tools
to appear next to the range bar,
allowing the user to animate the range bar. From left to right, the
tools are:
 Animation speed (slow = half the speed specified by pause,
normal= same speed as pause, fast = twice as fast as pause).
Defaults to normal. The speed can be changed by clicking on
the icon.
 Loop mode (causes the animation to loop). Defaults to true. It
can be changed by clicking on the icon.
 Reverse play. Starts the animation is reverse order. The button
changes to pause.
 Forward play. Starts the animation if forward order. The button
changes to pause.
EXAMPLE:
<RangeBar range min=0 max=200 start=20 finish=50
fgColor="0x0000DD" format="0.00m" align=$north/>
11.2.10
Button Elements
<Button/> extends <AbsButton>
SUMMARY:
Creates a push button.
<Button title="Apply" action={doApply()}/>
EXAMPLE:
<Button.toggle/> extends <AbsButton>
SUMMARY:
Creates a toggle button.
<Button.toggle title="Edit Mode"/>
EXAMPLE:
11.2.11
Feedback Elements
<ProgressBar/> extends <Comp>
SUMMARY:
Creates a progress bar. The progress can be updated by
incrementing the value property progressively.
PROPERTY
DESCRIPTION
string title
int min
int max
symbol orientation
The progress bar’s title.
The minimum possible value for the progress bar.
The maximum possible value for the progress bar.
The progress bar’s orientation, which may be $horizontal or
$vertical.
When true, the progress bar gives indefinite feedback. Otherwise,
it progresses from min to max. Defaults to false.
An arbitrary expression (action handler) which is evaluated when
the progress bar’s value changes. Progress bars don’t have event
handlers; use the action property instead.
boolean indefinite
vague action
EXAMPLE:
GUI Reference
<ProgressBar progress title="Updating" min=0 max=10
orientation=$horizontal action={doAction()}/>
449
11.2.12
Area Elements
<Area.text/> extends <AbsText>
SUMMARY:
Creates a text area, suitable for displaying multi-line text.
PROPERTY
DESCRIPTION
string file
int lineCount
Loads the contents of file whose path is denoted by file into the
text area. The file path may be absolute or relative to sys.root. In
a Flash client, the file must be present in a loaded partition.
The width of the tab character, in terms of spaces (defaults to 8).
The number of logical rows in the text area.
The number of logical columns in the text area.
You can append a string or a vector of strings to the text area by
setting this property. You can’t get this property. It’s more
efficient to append a vector of strings.
Maximum number of lines a text area will hold. When greater
than zero and exceeded, lines will be removed from the beginning
to keep the size within limit.
Number of lines in a text area.
EXAMPLE:
<Area.text comment value="sample comment"/>
int tab
int rows
int cols
string/vector add
int maxLines
<Area.url/> extends <AbsText>
SUMMARY:
Creates a URL pane, populated with data from the nominated URL.
PROPERTY
DESCRIPTION
string url
The URL for the pane, which may denote a file or web page
(defaults to "").
Scrolls to the HTML reference denoted by the value assigned to this
property. The value must be a # style HTML anchor. You can’t get
this property.
The event handler for this component has a different signature from
the standard event handler:
string scrollTo
EVENTS:
void handler (native comp, string url)
When the user clicks on a HTML anchor, the event handler is called
and the anchor reference is passed to the url parameter.
EXAMPLE:
11.2.13
<Area.url readOnly=true event=myHandler
url="www.acme.com/Sample.html"/>
Tree Elements
<Tree> extends <Comp>
SUMMARY:
Creates a tree, suitable for visualizing hierarchical information.
You can specify the tree nodes in three ways:
 By defining <Node> child elements.
 By specifying direct data. The data must be a vector of <Node>
elements, and can be nested.
 By defining a data model.
PROPERTY
450
DESCRIPTION
JavaGram Agile Development
vector data
Id model
Direct data for the tree. The tree uses this to build an internal
data model (defaults to null).
The data model for the tree, which must be a method in the same
class (defaults to null). It must have the following signature:
vague myModel (native tree, symbol cmd, native node,
vague subnode)
For the root node, node will be null. Possible values for cmd are:
 $count (return the count of the children of node).
 $get (return the child of node denoted by the subnode index;
vague select
vague root
boolean multiSelect
boolean reload
int rowHeight
EVENTS:
EXAMPLE:
the latter being an integer).
 $index (return the index of subnode in node, as an integer).
 $leaf (return true if node is a leaf node).
The currently-selected node(s). When multiSelect is true, the
result is a vector (may be empty). Otherwise, it’s a node (may be
null). For convenience, you can assign a node to this property
even when multiSelect is true.
The invisible tree root node. All tree nodes are descendants of
this node.
When true, multiple nodes can be selected by holding down the
control key and clicking. Otherwise, only one node can be
selected at a time.
When set to true, the tree is reloaded so that any change to the
data or data model is reflected. You can’t get this property.
The height of each row in pixels. In a non-Flash application,
when set to zero, the height of each cell is worked out
dynamically based on its icon height.
 $select is raised when a node is selected or deselected.
 $drill is raised when a node is double-clicked.
 $expand is raised when a node is expanded.
 $collapse is raised when a node is collapsed.
<Tree tree event=sampleHandler multiSelect=true>
<Node title="Customer">
<Node title="Account 1"/>
<Node title="Account 2"/>
</Node>
</Tree>
<Node>
SUMMARY:
PROPERTY
Creates a tree node. This can be used as data within a tree.
string title
vague image
The node’s title (defaults to "").
Optional image to be displayed to the left of the node title. This
may be either an <Icon> or the path of a GIF file.
GUI Reference
DESCRIPTION
451
vague altImage
string tooltip
vague presentation
vague content
vector children
native parent
boolean modified
boolean expand
boolean refresh
boolean reload
EXAMPLE:
11.2.14
Same as image, except that this is used when the node is selected.
Otherwise, image is used for both when the node is selected and
when it’s not selected.
The node’s tooltip.
The presentation for this node’s content (defaults to null).
Typically, this will be a GUI element, such as a panel that can
display the node’s content.
The logical data associated with the node (defaults to null).
Typically, when a node is selected, your application should
present a view of this data using the node’s presentation (e.g.,
populate a panel).
The children for this node, as a vector of <Node> elements. You
can also specify child elements by directly enclosing them in the
node.
The parent of this node, or null if a root node.
When true, it means that the node has been modified. A modified
node is highlighted by a ‘*” appearing before its title.
When true, the node is expanded so that its children are visible.
Refreshes the node and its children by redrawing them.
When set to true, the node (and its children) are reloaded so that
any changes are reflected. You can’t get this property. This is
similar to the reload property of a tree, except that it only affects
the node (and its children), not the rest of the tree.
<Node title="Help" image={sys.use("gifs/Book.gif")}>
<Node title="Topics"/>
</Node>
Table Elements
<Table/> extends <Comp>
SUMMARY:
Creates a table for visualizing tabular data.
PROPERTY
DESCRIPTION
vector data
Direct data form the table. This must be a vector of maps or a
vector of objects. The table uses this to build an internal data
model (defaults to null).
452
JavaGram Agile Development
Id model
The data model for the table, which must be a method in the
same class (defaults to null). It must have the following
signature:
vague myModel (native table, symbol cmd, int row, int col)
Possible values for cmd are:
 $rows (return the number of rows in the table).
 $cols (return the number of columns in the table).
 $get (return the data for the cell denoted by row and col).
 $put (update the model as a result of the user editing a cell).
The modified cell is denoted by row and col. The modified cell
value is denoted by the put property of the table. $put is not
used unless the editable property of the table is set to true.
 $style (return the style of the cell denoted by row and col).
The style must be either null (no style) or a vector of these
values: $plain, $bold, $italic, and hexadecimal
representation of a color value (e.g., "0xFF00FF"). $style is not
used unless the styled property of the table is set to true.
 $icon (return null or an <Icon> value). The icon is displayed
only when the column format has its $icon key set to true. The
vector format
GUI Reference
icon is displayed inside the cell and to the left of the cell
value.
 $sortAscend (sort the underlying data in ascending order of the
column denoted by col).
 $sortDescend (sort the underlying data in descending order of
the column denoted by col).
A vector that specifies the format of each column as a map. If
not specified, columns are formatted using default formatting
rules that are based on the type of data in cells. The map may
have any combination of the following keys:
 $key is used for data binding, and must map to a symbol in the
row data intended for the column.
 $title specifies the column title as a string.
 $width specifies the column width as an integer.
 $align specifies the alignment of the column, and must be
$east, $center, or $west.
 $format specifies the desired format of the cell as a string. See
sys.format().
 $icon optionally specifies whether the column cells have icons
(boolean value).
 $editor specifies an editor for the column cells. This must be
an element (e.g., <Field.text>) to be used for editing the cell.
453
vague select
boolean selectAll
int column
boolean multiSelect
boolean autoSize
boolean editable
boolean styled
list hitCell
vague sortAscend
vague sortDescend
int headingHeight
int rowHeight
vague put
string bgColorOdd
string bgColorEven
EVENTS:
454
The indices of the currently-selected table rows. When
multiSelect is true, this is a vector of integers (may be empty).
Otherwise, it’s an integer (or null). For convenience, you can
assign an integer to this property even when multiSelect is true.
When true, all rows are selected, provided multiSelect is also
true.
Returns the currently selected column, or null. You cannot set
this property.
When true, multiple rows can be selected by holding down the
shift or control key when clicking on rows. Otherwise, only one
row can be selected at a time (defaults to false).
When true, the columns are automatically resized to fill all the
available horizontal space for the table (defaults to false).
When true, the table cells can be edited by the user (defaults to
false).
When true, the cells can be individually styled according to the
styles specified by the data model (defaults to false).
A list of the format $(row, col) that denotes the last clicked
cell.
The table is sorted in ascending order of the column (column
index or key) assigned to this property. Set this property (or
sortDescend) to null if you want to disable sorting. Set it to -1 to
signify that the table is not sorted.
Returns the zero-based column number in which the table is
ascending-sorted, -1 if the table is not ascending-sorted, or null
if sorting is not allowed.
The table is sorted in descending order of the column (column
index or key) assigned to this property. Set this property (or
sortAscend) to null if you want to disable sorting. Set it to -1 to
signify that it’s not sorted.
Returns the zero-based column number in which the table is
descending-sorted, -1 if the table is not descending-sorted, or
null if sorting is not allowed.
The height of the table heading in pixels.
The height of each row in pixels.
The last modified cell value in the table, or null.
Background color for odd-numbered rows. The color must be in
hexadecimal format (e.g., "0x00FF00"). Defaults to light grey.
Background color for even-numbered rows. The color must be
in hexadecimal format (e.g., "0x00FF00"). Defaults to white.
 $select is raised when a row is selected or deselected.
 $drill is raised when a row is double-clicked.
 $hitCell is raised when a cell is clicked.
JavaGram Agile Development
<Table myTable model=tableModel format={myFormat}
editable=true multiSelect=false styled=true
event=sampleHandler/>
EXAMPLE:
Example of format vector:
vector<map> tformat = vector(
map($key => $name, $title => "Name", $width => 200,
$align => $west, $editor => textEditor)
,map($key => $age, $title => "Age", $width => 50,
$align => $east, $editor => numberEditor,
$icon => true)
,map($key => $sex, $title => "Sex", $width => 100,
$align => $west, $editor => sexEditor)
);
Examples of cell editors:
static <Field.text textEditor/>
static <Field.number numberEditor precision=0/>
static <Combo sexEditor data=["Male", "Female"]/>
11.2.15
Grid Elements
<Grid> extends <Container>
SUMMARY:
Creates a grid element. This is similar to a table but more
flexible, in that rows, columns, and cells can be individually
controlled. The following rules apply:
 If a cell doesn’t define a specific property then it’s inherited
from the cell’s column.
 If the column doesn’t define it either then it’s inherited from
the cell’s row.
 If the row doesn’t define it either then it’s inherited from the
default cell.
 If the default cell doesn’t define the property either then the
default value of the property is used.
These rules are useful in avoiding repetitions. For example, if
most of the cells in a column are dates, set the column’s kind to
be $date, and explicitly set the kind property of those cells that
are not dates.
PROPERTY
DESCRIPTION
boolean fixedWidth
When true, column boundaries are fixed. Otherwise, they can
be dragged by the user to resize them (defaults to false).
When true, row boundaries are fixed. Otherwise, they can be
dragged by the user to resize them (defaults to false).
When true, hot links within cells are underlined (defaults to
true).
When true, $hitCell events are generated even on locked cells
(defaults to false).
boolean fixedHeight
boolean underlineLink
boolean hitCellAll
GUI Reference
455
vague recalc
Id edit
When true, forces the grid to recalculate all cells that have a
calc property. This will not raise events, nor will it refresh the
cells. Returns the position of the cell being calculated as a list
of the form $(row, col).
The grid’d edit handling method. This must be a method in the
same class, with the following signature.
vague myEditHandler (native grid, int row, int col,
vague oldVal, vague newVal)
int rows
int cols
list hitCell
list modCell
EVENTS:
EXAMPLE:
When a cell is edited by the user, this method is automatically
invoked for that cell. The last four arguments denote the cell
position and its old and new values. This method must return
the desired new value for the cell, which can be oldVal, newVal,
or something else. Return $ignore if you want the change to be
ignored (e.g., when failing validation).
The number of rows in the grid (not settable).
The number of columns in the grid (not settable).
The position of the hit (activated cell) as a list of the form
$(row, col), or null.
The position of the last modified cell as a list of the form
$(row, col), or null. You can’t set this property.
 $hitCell is raised when an editable cell is clicked.
 $modified is raised when a cell’s value is modified.
 $calc is raised before calculation is performed as a result of a
cell having been modified.
 $postCalc is raised after calculation is performed as a result
of a cell having been modified.
<Grid grid hitCellAll=true fixedWidth=false
fixedHeight=true underlineLink=true
edit=myEditHandler event=myEventHandler>
//...
</Grid>
<GridElem>
SUMMARY:
PROPERTY
Abstract base for grid rows, columns, and cells.
symbol kind
The kind of the grid element data, which may be one of:
 $text (render as <Field.text>)
 $textArea (render as <Area.text>)
 $combo (render as <Combo>)
 $number (render as <Field.number>)
 $date (render as <Field.date>)
 $time (render as <Field.time>)
 $tick (render as <Option.tick>)
 $icon (render as an icon, specified via the image property of a cell)
Title for the element where relevant (e.g., for a tick box).
string title
456
DESCRIPTION
JavaGram Agile Development
vector data
Id model
boolean lock
boolean dim
int size
symbol align
native font
string format
string fgColor
string bgColor
string egColor
string lkColor
int border
string tooltip
real min
real max
int precision
int minPopupWidth
Data for the element where relevant (e.g., combo items).
Data model for the element where relevant (e.g., combo data
model).
When true the element is locked so that its content can’t be changed
(defaults to false).
When true the element is displayed as dimmed (defaults to false).
The width of the element (if a column or cell), or its height (if a
row).
The alignment of element content, which must be one of $center,
$north, $south, $east, $west, $northEast, $northWest, $southEast, or
$southWest (defaults to $west).
The desired font for rendering the element content (defaults to
null).
The format of the element content (defaults to null). See
sys.format().
Foreground color for the element (see <Comp> for color format).
Background color for the element (see <Comp> for color format).
Edge color for the element (see <Comp> for color format).
Color for the hot link in the element (see <Comp> for color format).
The thickness of the element border in pixels (defaults to 1). Use 0
for a borderless element.
The tooltip for the element.
The minimum value for a numeric element (defaults to 0). Follows
the same rules as <Field.number>.
The maximum value for a numeric element (defaults to the largest
real number). Follows the same rules as <Field.number>.
The number of decimal places for a numeric element (defaults to 2).
The minimum popup width for a combo element (defaults to 0).
<Column> extends <GridElem>
SUMMARY:
Creates a grid column element, which must be a child of a <Grid>.
EXAMPLE:
<Column kind=$text size=200/>
<Row> extends <GridElem>
SUMMARY:
Creates a grid row element, which must be a child of a <Grid>.
PROPERTY
DESCRIPTION
symbol key
The key (may be null) for binding this element to data (e.g.,
$details). To bind multiple rows that have the same structure to a
vector, give them the same key and make sure that the indexed
property is set to true. For example, if the grid contains three rows
that all have the key $products, then $products will denote a vector
(within the grid’s binding) of three elements each of which, for
example, is a map that represents a product.
boolean indexed When true, rows with the same key are indexed into a binding vector
(defaults to true).
GUI Reference
457
Type type
The type of the value that this element can be bound to (defaults to
map).
The zero-based index of the row in its parent grid, or -1 if the row has
no parent. You cannot set this property.
int row
<Row key=$details type=map>
//...
</Row>
EXAMPLE:
<Row.default> extends <Row>
SUMMARY:
Creates a default row, which must be a child of a <Grid>. This is
internally used as a template for creating new rows to be inserted into the
grid. To add a new row to a grid, use this syntax: grid += rowIdx, where
grid is the identifier for a <Grid> element and rowIdx is the zero-based
index for the new row. To delete a grid row, use this syntax: grid -=
rowIdx.
EXAMPLE:
<Row.default>
<Cell value="New Row"/>
<Cell value=200/>
<Cell value=true title="Valid"/>
<Cell value="Delete" link={grid -= grid.hitCell[0]}/>
</Row>
NOTE:
New rows can also be created and added at run time using this syntax:
grid += vector of lists
grid += list
grid += gridRow
where list’s first element is a new row and optional second element is an
index specifying the insert position. If the latter is not specified or is
greater then the size of the grid, the new row will be appended to the end
of the grid.
EXAMPLE:
grid += vector(
list((new GridRow()).getGridRow(), rowsInsIdx),
list((new GridRow()).getGridRow())
);
grid += list((new GridRow()).getGridRow(), rowsInsIdx);
// the following lines add 2 rows at the end:
grid += list((new GridRow()).getGridRow());
grid += (new GridRow()).getGridRow();
class GridRow {
<Row gridRow>
//...
</Row>
public native getGridRow() {
return gridRow;
}
}
458
JavaGram Agile Development
<Cell/> extends <GridElem>
SUMMARY:
Creates a grid cell. This must be a child of a <Row>.
PROPERTY
DESCRIPTION
symbol key
vague value
vague calc
The key (may be null) for binding this cell to data (e.g., $product).
The data binding (may be null) for this cell.
The calculation expression for this cell. This expression is evaluated
when any grid cell is modified or when the grid recalc property is set to
true, to produce the binding value for the cell. To remove a cell’s calc
effect, set it to null.
The column span for the cell (defaults to 1).
The hot link expression for the cell. When the cell is clicked, this
expression is evaluated. A cell that has a non-null link property is
highlighted similarly to a HTML hot link. To remove a cell’s link
effect, set it to null.
Image to be displayed within the cell, provided the cell’s kind is $icon.
This may be either an <Icon> or the path of a GIF file.
int colSpan
vague link
vague image
<Cell lock=false align=$east tooltip="Price" min=0.0
max=200.0 precision=2 calc={calcPrice()}/>
EXAMPLE:
<Cell.default/> extends <Cell>
SUMMARY:
Creates a default cell to hold default property values for all cells. If a cell
doesn’t have a property specified explicitly or implicitly (in its parent
<Column> or <Row>) then it inherits it from this element.
<Cell.default egColor="0x0000CC"/>
EXAMPLE:
11.2.16
Graph Elements
<Graph.line/> extends <AbsGraph>
SUMMARY:
Creates a line graph, where data points are joined by straight
lines. The graph can support multiple data series (i.e., multiple
lines). The minimum and maximum values for either axis are
automatically calculated from the data series. If the default values
(e.g., minKey) lie outside the calculated range, they are used
instead.
PROPERTY
DESCRIPTION
symbol kind
Denotes the kind of the graph, as one of $line or $bar (defaults to
$line). By setting this property, you change a line graph to a bar
graph and vice-versa.
Default minimum key for the x-axis (defaults to null).
Default maximum key for the x-axis (defaults to null).
Default minimum value for the y-axis (defaults to null).
Default maximum value for the y-axis (defaults to null).
When true, the graph is editable: the user can drag the data points
up or down to change their y-axis values, but not side-ways.
Defaults to false.
real minKey
real maxKey
real minValue
real maxValue
boolean editable
GUI Reference
459
boolean grid
When true, background grid lines are shown behind the graph
(defaults to false).
boolean hint
When true, the (x,y) value denoted by the mouse pointer is
displayed above the graph (defaults to true).
boolean pad
When true, pads all axes on either side by one major position
(defaults to false). This is especially useful for bubble graphs so
that the bubbles don’t obstruct the axes.
boolean
When true, displays minor division steps on the axes when there
showMinorSteps
is enough room (defaults to true). Otherwise, only major division
steps are shown.
string xTitle
The title for the x-axis (defaults to null).
string yTitle
The title for the y-axis (defaults to null).
int xMargin
The margin below the x-axis (defaults to 0).
int yMargin
The margin to the left of the y-axis (defaults to 0).
symbol xOrientation The orientation of labels for the x-axis (defaults to $horizontal).
Setting it to $vertical causes x-axis labels to be drawn
downwards.
vague xFormat
A string representing the format for rendering x-axis labels
(defaults to "0.0"), or a method in the same class returning a
formatted x-axis label. The method must have the following
signature:
string xFormatFun(native graph, real val)
vague yFormat
vague yEastFormat
Id bandFormat
vague select
Similar to xFormat, but for rendering y-axis labels.
Similar to yFormat, but for rendering east-side y-axis labels (if
any). An east-side y-axis is created if at least one of the series is
specified to use it (see the $east key of the style property).
A method in the same class returning a formatted description of a
band. The method must have the following signature:
string bandFormatFun(native graph, int bandIdx)
Get this property in response to a $select or $drill event. If the
user has clicked on a band then the list will of the form (bandId,
bandIndex). If the user has clicked on a graph series control point
then the list will of the form (seriesIndex, pointIndex). Otherwise,
it will return null. All indices are zero-based. Setting this
property has no effect.
460
JavaGram Agile Development
vector<map> style
A vector of maps, each of which specifies the style for a data
series (defaults to null). The map may have the following keys:
 $title specifies the title for the data series (defaults to null).
 $color specifies the line color (defaults to black).
 $dash specifies the dash size for the line (defaults to 0, i.e.,
solid).
 $weight specifies the thickness of the line (defaults to 1).
Setting it to 0 causes no lines to be drawn, but the data points
will still be visible if $marker is specified.
 $marker specifies the marker for each data point, which may be
one of $none, $circle, $square, $triangle, or $diamond (defaults
to $none).
 $fill specifies whether the marker should be drawn as solid or
outline (defaults to false).
 $east specifies whether the series y-axis should be on the
eastern side (defaults to false).
 $type optionally overrides the type of a series, and may be one
of: $line, $bar, or $bubble. When unspecified, it defaults to the
graph type. For example, in bar graph you can draw a series as
a line by setting its type to $line.
 $label (a boolean) specifies whether a series can have labels at
its value points (defaults to false). The actual labels are
provided by the graph model.
vector<map> bands
Similar to style but specifies bands for x and/or y-axis (defaults
to null). Each x-axis band is drawn as a vertical line, and each yaxis band is drawn as a horizontal line. Each band is described by
a map, which may have the same keys as described for style,
plus the following keys:
 $x specifies position of an x-axis band.
 $y specifies the position of a y-axis band.
 $id specifies the band’s unique ID.
vector<vector> data Direct data for the graph (defaults to null). This is used to build
an internal data model. The data must be a vector of vectors,
where each contained vector represents a series. The vector
indices serve as x-axis values, and the elements as the y-axis
values.
GUI Reference
461
Id model
The data model for the graph, which must be a method in the
same class (defaults to null). It must have the following
signature:
vague myModel (native graph, symbol cmd, int series,
int idx, double newVal)
Possible values for cmd are:
 $series (return the number of data series).
 $samples (return the number of samples in the series denoted by
series).
 $key (return the key for series at idx position, for x-axis).
 $value (return the value for series at idx position, for y-axis).
 $label (return the label(s) for series at idx position). This can
be either a single value or a list of up to two values. The first
value is displayed above the graph point, and the second value
(if present) is displayed below the graph point. In bar and
bubble graphs, the second value is displayed at the center of the
bar or bubble.
 $putable (return true if the value for series at idx can be
updated). When series is negative, idx denotes the zero-based
index of a band; otherwise, it denotes a zero-based index into
the series. This command is not invoked unless the editable
property of the graph is set to true. Using $putable, $putNext,
and $putLast gives you a much finer control over what part of a
graph can be edited than using direct data.
 $putNext (update a series or band at idx position to newVal in
response to the user dragging a series or band control point).
See also $putable for a description of series and idx. This
command is not invoked unless the $putable command returns
true. Use this command to provide additional feedback (if
needed) as the user drags a point.
 $putLast (similar to $putNext but invoked when the user
finishes dragging a control point by releasing the mouse
button). Use this command to commit or cancel the edit.
EXAMPLE:
<Graph.line bgColor="0xFFFFFF" xFormat=xFormatFun
yFormat=”HH:mm:ss” style={graphStyles} model=graphModel/>
where:
vector<map> graphStyles = [
[$title=>"Age", $color=>"0xFF0000", $dash=>0, $weight=>1,
$marker=>$circle],
,[$title=>"Income", $color=>"0x0000FF", $dash=>0,
$weight=>1, $marker=>$square]
];
string xFormatFun(native graph, real val) {
return val@int@string;
}
462
JavaGram Agile Development
<Graph.bar/> extends <Graph.line>
SUMMARY:
A variant of line graph where the data is displayed as vertical bars
instead of lines (the kind property defaults to $bar). The bars for values
belonging to different series are displayed next to each other.
<Graph.stack/> extends <Graph.line>
SUMMARY:
A variant of line graph where the data is displayed as vertical bars
instead of lines. The bars for values belonging to different series are
stacked on top of each other.
<Graph.bubble/> extends <Graph.line>
SUMMARY:
A variant of line graph where the data points are displayed as bubbles
(solid circles). Bubble graphs are not editable.
Whereas (for each series) a line graph specifies a y-value for a given xvalue, a bubble graph specifies a y-value as well as a bubble size and an
optional bubble color for a given x-value. Bubble graphs are useful for
visualizing multi-faceted data. For example, a line graph may be used to
show the GDP (y-axis) of countries (series) over a 10 year period (xaxis). A bubble graph can represent the same, but also show the average
per-capita income (bubble size), and the trade balance (bubble color) of
each country. The latter can range from green (trade surplus) to red (trade
deficit).
You should always use a data model (not direct data) for a bubble graph.
The data model should support additional cmds:
 $bubble (return the ‘bubble’ data). This can be a size number, or a list
of the form (size,color), where size denotes the relative size of the
bubble, and color optionally specifies a hexadecimal color string for
drawing the bubble. If color is not specifies, the style color of the
corresponding series is used instead.
 $bubbles (optionally return a legend vector). By default, a graph legend
is tied to the graph series. However, for a bubble graph whose bubble
colors are not tied to the series, you may want to provide a different
legend that specifies the meaning of each color. To do so, return a
vector in response to this model command. Each vector element must
be a map of the form [$color=>"...”, $title=>"..."], where $color
specifies a hexadecimal color string and $title denotes a string
describing the meaning of the color. For example, [$color=>"#FF0000",
$title=>"Trade Deficit"].
<Graph.pipe/> extends <AbsGraph>
GUI Reference
463
SUMMARY:
A graph where data is represented by horizontal pipes (is
especially useful for displaying heat maps). Each pipe may
consist of zero or more segments, where the segments are
typically drawn in different colors. A pipe graph has a single
horizontal axis (x-axis). Pipe graphs are not editable.
It’s recommended that you always enclose a pipe graph in a
<Pane.scroll horizontal=false>. This ensures that when there
are more pipes than can be displayed in the graph’s canvas, a
vertical scrollbar will appear.
PROPERTY
DESCRIPTION
real minValue
real maxValue
boolean grid
Default minimum value for the x-axis (defaults to null).
Default maximum value for the x-axis (defaults to null).
When true, background x-axis grid lines are shown behind the
graph (defaults to false).
When true, the title, value, and hint of the segment under the
mouse pointer is displayed above the graph (defaults to true).
When true, the pipe segments are gradient-filled (defaults to
true).
The title for the x-axis (defaults to null).
A string representing the format for rendering x-axis labels
(defaults to "0.0"), or a method in the same class returning a
formatted x-axis label. The method must have the following
signature:
boolean hint
boolean gradient
string xTitle
vague xFormat
vague select
string xFormatFun(native graph, real val)
Get this property in response to a $select or $drill event. If the
user has clicked on a segment then the list will of the form
(rowIdx, segmentIdx). Otherwise, it will be null. All indices are
zero-based. Setting this property has no effect.
vector<map> legend
A vector of maps, specifying the graph legend, which describes
the meaning of the colors used in the graph. The map may have
the following keys:
 $color specifies a color.
 $title specifies the meaning of the color.
symbol pipeTitlePos Denotes the position of pipe titles as one of: $north (title drawn
above the pipe), $west (title drawn to the left of the pipe), or
$none (no pipe title drawn). Defaults to $north.
vector<vector> data Direct data for the graph (defaults to null). This is used to build
an internal data model. The data must be a vector of vectors,
where each contained vector represents a pipe and is of the
format: [pipeTitle, segment1Map, segment2Map, …]. Each
segmentMap may have these keys:
 $value (segment value).
 $color (segment color).
 $title (segment title).
 $hint (segment hint).
464
JavaGram Agile Development
Id model
The data model for the graph, which must be a method in the
same class (defaults to null). It must have the following
signature:
vague myModel (native graph, symbol cmd, int row, int segment)
Possible values for cmd are:
 $count (when row is null, return the number of pipes;
otherwise return the number of segments for the pipe denoted
by row).
 $value (return the value for the segment denoted by row and
segment).
 $color (return the color for the segment denoted by row and
segment).
 $title (when segment is null, return the title of the pipe
denoted by row; otherwise return the title of the segment
denoted by row and segment).
 $hint (return the hint for the segment denoted by row and
segment).
EXAMPLE:
<Graph.pipe bgColor="0xFFFFFF" title="Sample Pipe Graph"
legend={pipeLegend} minValue=0 xTitle="Weight"
xFormat="0.0%" data={pipeData}/>
where:
vector<map> pipeLegend = [
[$color=>"0xFF0000", $title=>"Hot"],
[$color=>"0x00FF00", $title=>"Cold"],
[$color=>"0x0000FF", $title=>"Neutral"]
];
vector<vector> pipeData = [
["First Bar"
, [$value=>20.5, $color=>"0xFF0000", $title=>"A1"]
, [$value=>70.5, $color=>"0x0000FF", $title=>"A2"]
, [$value=>30.5, $color=>"0x00FF00", $title=>"A3"]
]
, ["Second Bar"
, [$value=>55.2, $color=>"0xFF0000", $title=>"B1"]
, [$value=>86.1, $color=>"0x0000FF", $title=>"B2"]
]
, ["Third Bar"
, [$value=>29, $color=>"0xFF0000", $title=>"C1"]
, [$value=>45.4, $color=>"0x0000FF", $title=>"C2"]
, [$value=>65, $color=>"0xFF0000", $title=>"C3"]
]
];
<Graph.pie/> extends <AbsGraph>
SUMMARY:
Creates a pie chart graph.
PROPERTY
DESCRIPTION
boolean drillable
When true, the pie segment under the cursor is highlighted
(defaults to false).
GUI Reference
465
string format
vague select
vector data
Id model
EXAMPLE:
The format for rendering pie labels (defaults to null).
Returns the zero-based index of the currently-selected pie
segment, or null. This property is settable.
Direct data for the pie chart (defaults to null). This is used to
build an internal data model. The data must be a vector of maps,
where each map may have these keys:
 $value (the data value).
 $title (the title of the pie segment).
 $color (the color of the pie segment).
The data model for the pie chart, which must be a method in the
same class (defaults to null). It must have the following
signature:
vague myModel (native pie, symbol cmd, int idx)
Possible values for cmd are:
 $count (return the number of data values).
 $value (return the value for the segment denoted by idx).
 $title (return the title for the segment denoted by idx).
 $color (return the color for the segment denoted by idx).
<Graph.pie format="0" model=pieModel drillable=true/>
<Graph.flow/> extends <AbsGraph>
SUMMARY:
Creates a flow graph, consisting of places and directed links
interconnecting the places. Places and links can be individually
selected or drilled. Flow graphs are useful for visualizing data
flow and work flow concepts.
PROPERTY
DESCRIPTION
boolean editable
When true, the graph can be edited (defaults to false). Editing
operations include: dragging a place to reposition it, dragging a
knob of a place to resize it, creating a link between two places by
dragging from the edge of a place to the edge of another place,
dragging a link segment to bend it (add a knob), dragging a knob
of a link to reconfigure it, dragging an end point of a link to
another place to change its source/target place.
When true, background grid lines are shown behind the graph
(defaults to false).
When set to $all, it resizes every place so that it nicely fits
around its title/description. Alternatively, you can set this
property to the index of a place, or a list or vector of place
indices, to limit resizing to those places only. You cannot get this
property.
boolean grid
vague autoSize
466
JavaGram Agile Development
vector data
Id model
Direct data for the flow graph (defaults to null). This is used to
build an internal data model. The data must be a vector of maps,
where each map specifies a place or link, and may have these
keys:
 $type (may be $place or $link).
 $shape (may be $box or $queue for a place, and $line for a link).
 $title (the title of the link or place).
 $desc (additional description, appearing below the title and
separated from it by a horizontal line).
 $color (the color of the boundary of place or the line of a link,
as a hexadecimal string).
 $bounds (a list of four integers identifying the boundary of a
place in (x, y, width, height) order).
 $ends (a list of two integers, denoting the zero-based index of
the source and target places for a link). If the link has
intermediate points then these follow in the format (x, y).
 $invert (a boolean which, when set to true, causes the place or
the link text to be displayed with an inverted background). Use
this as a way of highlighting an object.
The data model for the flow graph, which must be a method in the
same class (defaults to null). It must have the following
signature:
vague myModel (native flow, symbol cmd, int idx, map edit)
Possible values for cmd are:
 $count (return the number of graph objects).
 $type (return the type of the object denoted by idx).
 $title (return the title for the object denoted by idx).
 $desc (return the description for the object denoted by idx).
 $shape (return the shape for the object denoted by idx).
 $color (return the color for the object denoted by idx).
 $bounds (return the bounds for the place denoted by idx).
 $ends (return the source/target place indices for the link denoted
by idx).
 $invert (return the invert status for the object denoted by idx).
 $meta (return the meta data for the object denoted by idx).
 $data (return the data for the object denoted by idx).
 $edit (update the object denoted by idx using the edit map,
which may contain any key/value pair allowed in the data
vague click
GUI Reference
property).
When set to true, it puts the graph in ‘create’ mode. Provided the
graph is editable, clicking anywhere on the graph canvas causes a
$create event to be raised. The create mode is cancelled after the
event is fired or when you set this property to false.
467
EVENTS:
 $create is raised when click is set to true and the user clicks
on the graph canvas. You can obtain the click point as a list
of (x, y) format by getting the click property.
 $link is raised when the user holds the mouse button down
on a place edge and drags to another place and releases the
mouse button. You can obtain the source/target places as a
list of (srcIdx, tarIdx) format by getting the click property.
 $relink is raised when the user drags an end point of a link to
another place. You can obtain the old/new places as a list of
(oldIdx, newIdx) format by getting the click property.
EXAMPLE:
<Graph.flow editable=true data={fgData} event=fgHandler/>
11.2.17
Gadget Elements
<Gauge/> extends <Comp>
SUMMARY:
Creates a meter-style gauge, similar to those used in a car
dashboard. Use this component to visualize a value and colorcode its bands. The gauge value (denoted by its needle) can be
accessed via the value property (defaults to 0.0).
PROPERTY
DESCRIPTION
real min
real max
real target
The minimum value for the gauge (defaults to 0.0).
The maximum value for the gauge (defaults to 100.0).
The target value for the gauge (defaults to null). When specified,
this is shown as a white line on the gauge’s color band.
Optional title for the gauge, which is displayed below it (defaults
to null).
Direct data for the gauge (defaults to null). This is used to build
an internal data model. The data must be a vector of maps, where
each map represents a band on the gauge and may contain the
following keys:
string title
vector<map> data

$value must map to a real value, denoting the end of the band.

$title must map to a string, denoting the label for the end of
the band.

Id model
$color must map to a hexadecimal color string (e.g.,
"0xFF0000"), denoting the color of the band.
The data model for the gauge, which must be a method in the
same class (defaults to null). It must have the following
signature:
vague myModel (native gauge, symbol cmd, int idx)
Possible values for cmd are:
 $count (return the number of bands).
 $value (return the end value of the band denoted by idx).
 $title (return the title of the band denoted by idx).
 $color (return the color of the band denoted by idx).
468
JavaGram Agile Development
<Gauge gauge lay=$center title="Sample Gauge"
gaugeData={gaugeData} value=75/>
EXAMPLE:
where:
vector<map> gaugeData = [
[$value=>0.0, $title=>"0%", $color=>"0xFFFFFF"]
, [$value=>25.0, $title=>"25%", $color=>"0x990000"]
, [$value=>50.0, $title=>"50%", $color=>"0xFF0000"]
, [$value=>75.0, $title=>"75%", $color=>"0x0000FF"]
, [$value=>100.0, $title=>"100%", $color=>"0x00FF00"]
];
11.2.18
Map Elements
<GoogleMap/> extends <Comp>
SUMMARY:
Creates an element for displaying a Google map.
Note: To use this component, you must have JDICplus.jar and
JDICplus_native_applet.jar in your class path (same is required
for JADE). Also, because this component uses the IE DLL, if
you use a proxy server for Internet access, you must ensure that
it’s correctly specified in your Internet Explorer configuration.
PROPERTY
DESCRIPTION
string key
Denotes the map key. To get a valid map key for your domain,
visit http://code.google.com/apis/maps/signup.html. Please note
that a key only works for the specific domain for which it’s
generated (e.g., ‘http://www.mydomain.com’). The map key
must be set before accessing any other property.
list at
Specifies the center of the map as a list of two elements:
(latitude, longitude), both of which must be real numbers.
This property should not be accessed until the map is ready.
boolean navControl
When set to true, the map will include a navigation control for
zooming and panning (defaults to false).
boolean typeControl When set to true, the map will include a map type control for
selecting the type of the map (defaults to false).
int zoom
Denotes the map zoom level, as a number in the range 1-19
(defaults to 1).
vague geocode
To find out the geo-coordinate of a location (e.g., country, state,
suburb, address), set this property to a string representation of it.
This property should not be accessed until the map is ready. The
response (reported via the $geocode event) can be accessed by
getting this property, which will be a map having these keys:
$request (denotes the original request string), $error (denotes an
error string when an error occurs), $response (denotes a vector
when the request is successful). Each element of the vector
denoted by $response is a list of three elements:
(resolvedAddress, latitude, longitude).
GUI Reference
469
vector markers
EVENTS:
Denotes the markers for the map, each being an instance of
<MapMarker>. This property should not be accessed until the map
is ready.
 $ready is raised when the map is ready for use.
 $geocode is raised when the response to a geo-coordinate
request (by setting the geocode property) becomes available.
EXAMPLE:
<GoogleMap key="..." event=mapHandler/>
<MapMarker/> extends <AbsComp>
SUMMARY:
Creates a marker for a Google map. Because a marker can’t be
added to a map until the map is ready, it’s best to create a marker
using gui.create() after the map becomes ready. To add a
marker to a map, use the syntax map += marker. To remove it, use
the syntax map -= marker. You can also create a collection of
markers and add them in one go by setting the markers property
of a map.
PROPERTY
DESCRIPTION
list at
Specifies the position of the marker on the map as a list of two
elements: (latitude, longitude), both of which must be real
numbers.
The alignment of the marker image relative to the map
coordinate it represents; must be one of $center, $north, $south,
$east, $west, $northEast, $northWest, $southEast, or $southWest
(defaults to $northWest, i.e., the marker’s top-left corner
coincides with the coordinate).
See description in <Comp>. For a default marker, this represents
the border color of its image.
See description in <Comp>. For a default marker, this represents
the fill color of its image.
Denotes the marker radius.
When true, the marker is drawn with a shadow (defaults to true).
Optional image to be used instead of the default marker image.
This may be either an <Icon> or the path of a GIF file. Note that
if title is also set, the default marker is used instead.
Denotes the title that appears on the marker. When this property
is set, the effect of image is cancelled and the default marker is
used instead.
Denotes the marker tooltip text.
Denotes the <Font> to be used for title.
Specifies whether the marker is visible on the map (defaults to
true).
Setting this property to a string causes an information window to
appear above the marker, containing the string. Setting it to null
causes the window to disappear. If the string starts with <html>, it
will be rendered as HTML.
symbol align
string fgColor
string bgColor
int radius
boolean shadow
vague image
string title
string tooltip
native font
boolean show
string info
470
JavaGram Agile Development
boolean clickable
boolean draggable
Id event
EVENTS:
EXAMPLE:
Specifies whether the marker is clickable (defaults to true).
Markers that are not clickable generate no $click or $drill
event, nor cause the hand cursor icon to appear when the cursor
hovers over them.
Specifies whether the marker can be dragged around the map
(defaults to false). Markers that are not clickable generate no
$click, $drill, or $drag event, nor cause the hand cursor icon to
appear when the cursor hovers over them.
The event handler for this element, which must be a method in
the same class.
 $select is raised when the marker is clicked, provided the
clickable property is true.
 $drill is raised when the marker is double-clicked, provided
the clickable property is true.
 $drag is raised when the marker is dragged, provided the
draggable property is true.
 $rollOver is raised when the mouse rolls over the marker.
 $rollOut is raised when the mouse rolls out of the marker.
gui.create($MapMarker, map($at=>$(48.85, 2.34),
$image=>sys.use("lib/gifs/House.gif"),
$event=>Util.strToId("markerHandler")), this);
<MapMarker.default/> extends <MapMarker>
SUMMARY:
Defines a map marker whose properties provide default property
values for subsequent markers of any map (excluding at, show,
image, info, and event properties). The default values take effect
when the marker is added to a map (any map!), and lose their
effect when it’s removed from a map.
<MapRegion/> extends <AbsComp>
SUMMARY:
Creates a region for a Google map. A region is a closed polygon
and behaves similarly to a marker.
PROPERTY
DESCRIPTION
vector<list> bounds
Specifies the region’s boundary as a vector of coordinates, each
of which must be a list of two elements: (latitude, longitude),
both of which must be real numbers.
To test if a coordinate is within the region, set this property to
the coordinate as a list of the format (latitude, longitude), and
then get the property as a boolean.
See description in <Comp>.
See description in <Comp>.
Specifies whether the region is visible on the map (defaults to
true).
The event handler for this element, which must be a method in
the same class.
vague test
string fgColor
string bgColor
boolean show
Id event
GUI Reference
471
gui.create($MapRegion, map($bounds=>vec), this);
EXAMPLE:
11.2.19
Jade Elements
<Jade.editor/> extends <Comp>
SUMMARY:
Creates a syntax highlighting editor for viewing and editing
JavaGram code, and real-time highlighting of errors. The editor
can be configured to show three things:
 Editing pane (center)
 Gutter for showing line numbers (left)
 Ruler for showing errors/warnings and navigating to them
(right).
PROPERTY
DESCRIPTION
boolean readOnly
When false, the code displayed by the editor can be edited
(defaults to false).
When true, the gutter to the left of the editor pane is shown
(defaults to true).
When true, the ruler to the right of the editor pane is shown
(defaults to true).
When true, tabs and end-of-lines are shown as visible characters
(defaults to false).
When true, a dimmed horizontal line is drawn before each class
and each method, and a dimmed vertical line is drawn to mark
the position of cols (defaults to true).
The position of the guide column (defaults to 120). This is not a
physical limit on line length, but simply a guide.
The size of the tab character in terms of spaces (defaults to 4).
The validation level, which may be one of these symbols:
 null (no validation)
 $syntax (syntax rules validation only). This is the default.
 $semantics (syntax and semantics rules validation)
A list of two integers that, respectively, denote the beginning
and end of the current selection; both are zero-based offsets
from the start of the file. The selection is shown as:
 A blinking caret if the two offsets are the same (i.e., empty
selection).
 A highlighted text segment (can expand over multiple lines) if
the two offsets are different.
boolean showGutter
boolean showRuler
boolean showMarkers
boolean showGuides
int cols
int tabSize
symbol validation
list select
472
JavaGram Agile Development
vague selectVal
EVENTS:
Getting this property returns null when the current selection is
empty, and a string representation of the selection otherwise.
You can set this property to one of these:
 null or $delete (causes the current selection, if any, to be
deleted).
 $copy (copies the current selection to the clipboard).
 $cut (removes the current selection and places it on the
clipboard).
 $paste (replaces the current selection with what’s on the
clipboard).
When true, all text is selected (defaults to false).
To load a file into the editor, set this property to the file path.
Returns the path of the currently-loaded file. Defaults to null.
To save the editor content to a file, set this property to the file
path. Returns the path of the currently-saved file. Defaults to
null.
When true, it means that there are unsaved changes (defaults to
false).
To undo editing events, set this property to the count of events
you want undone. Returns the number of events that can be
undone (defaults to 0).
To redo editing events, set this property to the count of events
you want redone. Returns the number of events that can be
redone (defaults to 0).
 $modified is raised when the content is modified.
EXAMPLE:
<Jade.editor editor event=editHandler/>
boolean selectAll
string open
string save
boolean modified
int undo
int redo
11.2.20
Reference Elements
<Indirect/> extends <AbsComp>
SUMMARY:
Allows you to use an element created elsewhere (in the same or another
class). You can also specify any of the <Comp> properties in this element.
However, these must appear after the ref property, and are assigned to
the element denoted by ref.
PROPERTY
DESCRIPTION
native ref
The element being referenced. The effect is as if the referenced element
had been defined in place of <Indirect>.
EXAMPLE:
<Indirect ref={gridPanel.panel}/>
where gridPanel is an object whose panel field denotes a <Panel>.
<NativeComp/> extends <Comp>
SUMMARY:
Allows you to create an element whose implementation resides
externally (e.g., in a JAR file).
PROPERTY
GUI Reference
DESCRIPTION
473
native ref
The externally-implemented component. This is usually set to the result
of a sys.java() call.
EXAMPLE:
<NativeComp ref={sys.java("com.metaphaseeditor.MetaphaseEditor",
$native, "new")}/>
11.2.21
Invisible Elements
<Null/> extends <Comp>
SUMMARY:
Creates an invisible element. This is useful when we need a valid but
‘empty’ component (e.g., inside a <Pane.split>).
EXAMPLE:
<Null/>
<Plate/> extends <Comp>
SUMMARY:
Creates a boiler plate. This is useful for specifying properties such that
they can be shared by other elements.
EXAMPLE:
<Plate Req fgColor="0xFF0000" tooltip="Required"/>
<Req:Label title="Surname"/>
<Timer/>
SUMMARY:
Creates a timer, which is a simple wrapper of Java’s Swing Timer
class. This is similar to Timer, except that the event handler
executes on the Swing event dispatching thread.
PROPERTY
DESCRIPTION
int delay
EVENTS:
The delay, in milliseconds, between timer events firing (defaults to
10,000 milliseconds).
The initial timer delay, in milliseconds (defaults to delay).
When true, the timer fires only once (defaults to false).
When set to true, the timer starts. Returns true if the timer is still
running.
When set to true, the timer stops. Returns true if the timer has
stopped running.
May be set to one of $auto, $manual, or $now (defaults to $manual).
When set to $auto, the timer automatically restarts as a result of
explicit or implicit gui.maintain(). When set to $manual, the
timer’s activation can only be controlled via the start and stop
properties. Setting this property to $now causes the timer to restart
immediately.
The timer event handler, which must be a method in the same class.
 $do is raised when the timer fires.
EXAMPLE:
<Timer timer delay=2000 event=sampleHandler once=true/>
int initialDelay
boolean once
boolean start
boolean stop
symbol restart
Id event
<Worker/>
474
JavaGram Agile Development
SUMMARY:
Creates a worker, which is a simple wrapper of Java’s
SwingWorker class. This is useful for performing lengthy tasks that
would otherwise cause the GUI to freeze while the task is
executing. Unlike normal threads, SwingWorker executes on
Swing’s event dispatcher thread, thus enabling it to access GUI
components. Because of the single-threaded nature of the Flash
Player, this element will execute synchronously in a Flash client.
PROPERTY
DESCRIPTION
boolean run
When set to true, the worker is created and executes. Returns true if
the worker is still running.
When set to true, the worker aborts (if it’s still running). Returns
true if the worker has been cancelled.
This can be set to a value in the range 0 to 100, which causes the
worker method to be later invoked (cmd = $progress). Returns its
last value (or 0).
You can set this property to an arbitrary value, causing the worker
method to be later invoked (cmd = $process). Returns its last value
(or null).
Returns the value returned by the worker method in response to $do.
Caution: getting this property blocks the thread until the worker
completes execution. You can’t set this property.
The worker’s handler, which must be a method in the same class,
having the signature:
boolean cancel
int progress
vague publish
vague get
Id worker
vague worker (symbol cmd, vague val)
This method is invoked by the JavaGram framework. Possible
values for cmd are:
 $do is raised when the worker is run, in which case val is null.
You should not attempt to do GUI operations in response to $do.
 $done is raised when the worker has finished, in which case val is
null.
 $progress is raised when a value is assigned to the progress
property, in which case val denotes the progress value. This
gives you the opportunity to visually display the level of progress
so far. You can safely do GUI operations in response to
$progress.
 $process is raised when a value is assigned to the publish
property. It’s possible that $process is raised once for multiple
assignments. Therefore, val is always a vector of the latest
assigned values. This gives you the opportunity to process data
published so far.
EXAMPLE:
GUI Reference
<Worker search worker=searchWorker/>
475
11.3 The Gui Pseudo Class
11.3.1 Gui Attributes
GUI attributes denote useful values that are either set internally. Unless specified
otherwise, GUI attributes are read-only.
list gui.screen
Summary: Denotes the screen size in pixels, as a list of two integers: (width, height).
11.3.2 Gui Methods
void gui.run (native app)
SUMMARY:
Runs the <App> denoted by app in a separate thread and returns
immediately.
EXAMPLE:
<App app>
//...
</App>
//...
gui.run(app)
symbol gui.tag (native comp)
SUMMARY:
Returns the tag of an element as a symbol.
EXAMPLE:
<Option.radio r/>
//...
gui.tag(r)
// returns: ${Option.radio}
native gui.create (symbol tag, map<symbol,vague> props = null, vague context = null)
476
JavaGram Agile Development
SUMMARY:
Creates a GUI element procedurally (rather than declaratively) and returns
it. The element’s tag name must be specified as a symbol. For qualified
elements, use the escape notation (e.g., ${Area.text}), or use underscore
instead of dot (e.g., $Area_text). The desired properties should be
provided as a map, where each property name is specified as a symbol. If
the component refers to a class method (e.g., event handler), you should
pass the class context, which may be a class name (sufficient for static
method) or an object (required for non-static methods). For a property that
expects an ID (e.g., event), use Util.strToId() (as defined
lib/lang/Common.jag) to construct the ID.
Note: when you create a GUI element declaratively, the properties are
applied in the order declared. Because a maps keys are always sorted,
gui.create() will apply the properties denoted by props not in the order
you write them, but in lexicographic order. To force a different order,
exclude the properties that you want to be set last and then set them after
the element is created.
Wherever possible, you should create GUI elements declaratively. Use
gui.create() only when you need to create an element dynamically, such
as in response to some user action. On rare occasions, you may need to
create a GUI element on the server side (e.g., to produce an image from a
graph, for insertion into a report). Because declarative GUI elements are
filtered out of server code, gui.create() is the only way you can create
server-side GUI elements.
EXAMPLE:
gui.create($Node, [$title=>"Sample"])
vague gui.get (native comp, symbol prop)
SUMMARY:
Returns comp GUI element’s property value denoted by prop.
EXAMPLE:
gui.get(node, $title)
vague gui.set (native comp, symbol prop, vague value)
SUMMARY:
Sets comp GUI element’s property value denoted by prop to value.
EXAMPLE:
gui.set(node, $image, sys.use("gifs/OpenFolder.gif"))
native gui.getCell (native grid, int row, int col)
SUMMARY:
Returns the cell in grid denoted by row and col, both of which are zerobased, or null if the values are out of range.
EXAMPLE:
gui.getCell(grid, 0, 2)
boolean gui.bind (native comp, vague record)
GUI Reference
477
SUMMARY:
Binds the element denoted by comp to the data denoted by record. The
types must be consistent. For example, when comp is a <Field.text>,
record must be a string, and when comp is a <Panel>, record must be a map
or object. For composite elements, binding is done recursively and
hierarchically, by matching the key property of each child element against
the same map key or field name in the data.
EXAMPLE:
gui.bind(panel, data)
boolean gui.save (native comp, vague record)
SUMMARY:
Performs the reverse of gui.bind() by saving the information present in
comp into record. The same rules apply.
EXAMPLE:
gui.save(panel, data)
void gui.maintain (native comp, boolean force = false)
SUMMARY:
Causes the visible and enable properties of comp (and its children, if any)
to be evaluated, thus changing the visual state of the element(s). This is
normally called by the framework for you, but there may be situations
where you want to explicitly call it. When force is true, the two properties
are evaluated regardless of the state of the element.
EXAMPLE:
gui.maintain(panel)
void gui.saveImage (native comp, string imagePath, int width = null, int height = null,
symbol callback = null, list extraCBArgs = null)
SUMMARY: Saves the image of the element denoted by comp in the file whose absolute
path is denoted by imagePath. The currently supported formats are GIF and
PNG, so imagePath must have one of these two extensions. You can specify
the desired size of the image using width and height. Otherwise, the actual
size is used. When width and height are zero (e.g., the element not yet
rendered) these default, respectively, to 600 and 400.
In a Flash client, width and height are ignored. Furthermore, the image is
sent to the server to be saved as a file whose relative location is denoted by
imagePath.
Upon saving the image, callback is called if present. This should be a
method in the same class, and must have at least a vague parameter,
followed by optional parameters if desired (use extraCBArgs to pass values
for the additional parameters as a list of arguments). The first parameter
receives either an Exception or an information map of the form:
EXAMPLE:
[$local=>strPath, $remote=>strPath, $size=>intVal, $timestamp=>intVal]
gui.saveImage(panel, "C:/temp/PanelImage.gif")
void gui.transformImage (string srcImage, list transform, string dstImage,
int dstSize = null, symbol readyCallback = null, list extraCBArgs = null)
478
JavaGram Agile Development
SUMMARY:
Transforms srcImage according to transform and writes the result to
dstImge path with the specified width and height, where transform may be
a list of the following transformation instructions:
 ($translate dx dy) translates the image by real values dx and dy.
 ($scale sx sy) scales the image by real values sx and sy.
 ($rotate angle) rotates the image real angle expressed in radians
(positive angle rotates in clockwise direction).
 ($rotate angle x y) rotates the image about real coordinates x and y
by real angle expressed in radians.
When dstSize is not specified, it’s inherited from the source image, but
adjusted for scaling. When transform is null but dstSize is specified, the
image is scaled so that its diagonal size matches dstSize.
When readyCallback is specified, it is called when the target image has
been converted. You should use it in AIR deployments, because of
asynchronous behavior. The callback can also be declared with extra
parameters, in which case, matching arguments must be provided as list
for extraCBArgs.
Note that source and target images need not be of the same format. For
example, if the source image is GIF and the target is PNG, format
conversion will take place. Supported image formats include: GIF, JPG,
and PNG.
EXAMPLE:
gui.transformImage("C:/temp/PanelImage.gif",
$(($scale, 0.5, 0.5)($rotate, 3.14)),
"C:/temp/PanelImage2.jpg")
string gui.imageAsHex (native comp, int width = null, int height = null)
SUMMARY:
Same as gui.saveImage() except that instead of saving the image to a file,
a string version of the image is returned. This string is encoded as
readable hexadecimal values, suitable for embedding in a file. The
assumed image format is PNG for Java runtime and JPG for Flash
runtime.
EXAMPLE:
gui.imageAsHex(panel)
void gui.onPreview (symbol callback)
SUMMARY:
This method is for Flash clients (has no effect in Java clients). It defines a
callback for handling component preview requests, which should have the
signature:
void previewHandler (native comp)
The component may be a graph or a Google map component. The handler
is invoked when the user double-clicks the component, receiving the
component as argument.
EXAMPLE:
gui.onPreview($previewHandler)
void gui.toHtml (native comp, stream out = null, string dir = null)
GUI Reference
479
SUMMARY:
Writes an HTML representation of comp and its data to the stream denoted
by out, or sys.out when out is null. It ignores containers whose report
property is set to false. The dir parameter specifies the directory into
which the caller intends to place the HTML file (defaults to the current
user directory) – any generated image files are placed in this directory.
EXAMPLE:
gui.toHtml(panel)
symbol gui.alert (string title, string message, native owner, symbol kind,
symbol res
SUMMARY:
This is a dynamic version of <Alert/>. It’s useful for one-off situations
where you don’t want the overhead of creating an element. Only four of
the <Alert/> properties are supported. The alert box contains one button,
unless kind is set to $question, in which case it contains three buttons:
“Yes”, “No”, and “Cancel”. The method returns a symbol that denotes the
pressed button (i.e., one of: $ok, $yes, $no, $cancel). If the alert is simply
close, $ok or $cancel is returned. In a Flash client, always null is returned.
You can optionally provide a resultCallback, which should denote the
name of a method in the same class, as a symbol, having the signature:
void callback (symbol result)
When the alert is closed, this method is called with the alert result as
argument. The callback can also be declared with extra parameters, in
which case, matching arguments must be provided as list for extraCBArgs.
EXAMPLE:
gui.alert("Save", "Save changes?", mainFrame, $question)
void gui.loadFont (string fontSwfFile, string fontName, symbol callback = null)
SUMMARY:
This function is intended for Flash clients (in native clients it won’t do
anything) and allows you to load an embedded font that has been
packaged as an SWF file, whose path is denoted by fontSwfFile.
You can optionally provide a callback, which should denote the name of a
method in the same class, having the signature:
void callback ()
If the font is successfully loaded, the callback (if any) is called.
EXAMPLE:
480
gui.loadFont("C:/flash/_MSGothic.swf", "_MSGothic", $onLoad)
JavaGram Agile Development
NOTE:
To package a font as an SWF file, create an ActionScript project in the
Flex IDE for Eclipse and define an ActionScript class like this:
package {
import flash.display.Sprite;
import flash.text.*;
public class _MSGothic extends Sprite {
[Embed(source='C:/fonts/msgothic.ttf', fontName='_MSGothic')]
private static var font:Class;
public static function getFont():Class {
return font;
}
}
}
The fontName should be chosen such that it doesn’t clash with device
fonts. To ensure this, it’s recommended that you start it with an
underscore.
boolean gui.camera (symbol action, symbol callback = null)
SUMMARY:
This method is only available for AIR on a mobile device. To register a
camera, pass $register for action and a valid callback of the signature:
Void cameraCallback (string image)
If the registration is successful, the method will return true. Otherwise
(e.g., if the device has no accessible camera) it will return false.
To take a picture, pass $launch for action. This will display the device’s
user interface for taking a picture. Once the picture is taken and accepted,
the callback method will be called with the URL of the image file.
EXAMPLE:
gui.camera($register, $camCallback)
boolean gui.geoLocation (symbol callback)
SUMMARY:
This method is only available for AIR on a mobile device. To register a
listener pass a callback of the signature:
void geoCallback (map<symbol,real> details)
If the registration is successful, the method will return true. Otherwise
(e.g., if the device has no accessible GPS) it will return false.
When the device’s geo-coordinate changes, the callback is called with a
map having the following keys:
 $latitude denotes latitude in meters.
 $longitude denotes longitude in meters.
 $altitude denoted altitude in meters.
 $speed denotes speed in meters per second.
 $heading denotes the direction of movement (with respect to true
north) in integer degrees (null on Android).
 $hAccuracy denotes the horizontal accuracy in meters.
 $vAccuracy denotes the vertical accuracy in meters.
EXAMPLE:
GUI Reference
gui.geoLocation($geoListener)
481
12
SQL Reference
This chapter specifies the database access features of JavaGram. Use this chapter to look
up SQL topics once you’ve become familiar with the language. For a tutorial style
introduction to JavaGram’s SQL capabilities please refer to Chapter 5.
None of the SQL facilities described in this chapter is available in the Flash JavaGram
runtime.
12.1 Query and Transaction Statements
The query and transaction statement blocks respectively detailed in Sections 10.7.15 and
10.7.16 delimit scope around database operations to control the allocation and deallocation of connections, statements, and result sets, and finally the timing of database
commits. Failure to enclose queries and transactions within appropriate delimiting blocks
will result in runtime exceptions.
12.2 Markup
Qualified <text> members can be used to create parameterized SQL. In addition to the
facilities described for text members in Section 10.8.6, the substitution of parameters
within dynamic SQL and prepared statements is catered for.
12.2.1 Field Translation
Field names in queries are automatically translated according to these rules:

An all-lowercase field will remain unchanged (e,g., salary).

A field containing underscores is translated such that it becomes all-lowercase, except
for letters immediately succeeding underscores, which become uppercase (e.g.,
total_amount and Total_AMOUNT will both become totalAmount).

To keep an underscore unchanged, use a dash instead (e.g., TOTAL-AMOUNT becomes
total_amount).

The above translations are applied to the field names as specified by the query,
including any renaming specified using the SQL as command (e.g., in select name as
client_name from client, the effective field name is client_name which is then
translated to clientName; to keep the underscore, write the query as select name as
"client-name" from client).
12.2.2 Text.sql
This element is used to return a string of dynamic SQL. An arbitrary number of
parameters are substituted into the text, with automatic escaping for strings where
parameters are enclosed within {` and }. In the interests of avoiding inadvertent errors,
482
JavaGram Agile Development
enclosing between { and } is not permitted. For the infrequent cases where substitution
without escaping is required, the variable should be enclosed between {= and }. Example:
<text.sql string deleteTable(string tableName)>
drop table {=tableName}
</text.sql>
<text.sql string getUpdateSQL(int orderNum, string comment)>
update order_table
set
comment = {`comment},
visited = visited + 1,
updated_at = now()
where order_num = {`orderNum}
</text.sql>
Usage:
transaction ($oracleDb) {
string sqlCmd = getUpdateSQL(13162, "urgent delivery");
int rowCount = sql.execUpdate($oracleDb, sqlCmd);
}
12.2.3 Text.sql.query
This element offers the capabilities of the text.sql element in creating dynamic SQL but
rather than returning the string, it executes the generated query against the specified
database. Specification of the database through the db property is mandatory with this
element.
The method is defined with the return type being native, because a result set is expected
to be returned. Example:
<text.sql.query native findUser(string user) db=$mysqlDb>
select *
from application_user
where user_name = {`user}
</text.sql.query>
Usage:
query ($mysqlDb) {
native rs = findUser("jan.brown");
if (sql.next(furs))
sys.println(sql.getRow@map(rs));
}
SQL Reference
483
12.2.4 Text.sql.update
This element is very similar to the text.sql.query element, except that it’s used for
updates rather than queries. Accordingly, it returns an int representing the number of
rows affected rather than a native. Example:
<text.sql.update int updateSurname(string user, string surname) db=$mysqlDb>
update application_user
set surname = {`surname}
where user_name = {`user}
</text.sql.update>
Usage:
query ($mysqlDb) {
int rowsUpdated = s.updateSurname("u1234", "Atkins");
}
12.2.5 Text.sql.prepare
This element is used to create a prepared statement that may later be executed one or
more times with different parameters. Parameters escaped by being enclosed between {?
and } generally refer to member variables within the class. These are not substituted at
the time of preparing the statement but rather are assigned at the time the SQL is
executed. Example:
class MyClass {
final private Delivery d = new Delivery();
<text.sql.prepare native getInsertStmt() db=$oracleDb>
insert into deliveries (consignment, distance, rate, note_date)
values ({?d.consignment}, {?d.kms}, {?d.rate}, now())
</text.sql.prepare>
//...
}
Usage:
transaction ($oracleDb) {
native ps = getInsertStmt();
sql.execUpdate(ps);
}
12.2.6 Text.sql.callable
This element is used to create a callable statement, enabling the calling of stored
procedures. This element is used very much like text.sql.prepare, the difference being
that the return value can later be used to set output as well as input parameters. Example:
<text.sql.callable native getCallableStmt() db=$oracleDb>
484
JavaGram Agile Development
call sp_get_account_transactions (
{?b.accountNum}, {?b.dateFrom}, {?b.dateTo}, {?b.out}
)
</text.sql.callable>
Usage:
query ($oracleDb) {
native callable = getCallableStmt();
sql.registerOutParam(callable, 4, "oracle.jdbc.OracleTypes.CURSOR");
sql.exec(callable);
vague result; // either an int or a result set
while ((result = sql.nextResult(callable)) != null) {
if (result instanceof int)
sys.println("Update count: ", result);
else
while (sql.next(result))
sys.println(sql.getRow@map(result));
}
result = sql.get(callable, 4); // output param 4 is a result set
while (sql.next(result))
sys.println(sql.getRow@map(result));
}
12.2.7 Element Properties
For all but the text.sql element, the db property must be specified to allow the element to
be applied to the correct database. In most cases, additional properties are not required
but can optionally be specified.
Here is an example showing many of the available properties:
<text.sql.prepare native getPrepStmt()
db=$mysqlDb
timeout=10
// Timeout set to 10 seconds
concurrency=$readOnly
holdability=$closeCursorsAtCommit
type=$forwardOnly>
//...
</text.sql.prepare>
The following table summarizes the available properties.
Property
db
timeout
SQL Reference
Description
This property is set to a symbol that refers to a section within the
application configuration file specifying database URL, username,
password, and the like.
This property allows specifying a timeout period in seconds for
when the statement is executed.
485
concurrency
This property controls the capabilities of result sets returned after
the query is executed. Possible values are
 $readOnly the default value, result set may not be updated.
 $updatable result set can be updated.
This property is not normally needed but can be used for fine
control of the timing of closing result sets relative to commits.
Possible values are:
 $holdCursorsOverCommit
 $closeCursorsAtCommit
The default is determined by the underlying database and JDBC
driver.
By default, result sets are accessed only by using sql.next(),
moving from the first row forward. This property can be used to
allow the result set to be accessed in other ways, using one or more
of sql.previous(), sql.first(), and sql.last(). Possible values
are:
 $forwardOnly is the default.
 $scrollInsensitive allows the other access methods but is
generally insensitive to data changes underlying the result set.
 $scrollSensitive is generally sensitive to underlying data
changes.
holdability
type
12.2.8 Capability Summary
The following table summarizes the return type, permissible properties, and valid escape
syntax for each SQL element type.
Element:
Returns:
text.sql text.sql.query text.sql.update text.sql.prepare text.sql.callable
string
native
int
native
native
(result set)
(prepared stmt) (callable stmt)
Property
db

required
required
required
required
timeout

optional
optional
optional
optional
concurrency

optional

optional
optional
holdability

optional

optional
optional
type



optional
optional
Escaping
{?…}





{`…}





{=…}





486
JavaGram Agile Development
12.3 Database Connection Configuration
The –config command line option can be used to nominate a program configuration file,
specified as a map. Within this map, the $database key points to a map of maps, each
specifying the configuration of a database connection.
In the example below, there is one database connection specification called $app2Db,
which depicts a standard set of keys known to the JavaGram runtime:

$name (a descriptive name)

$driver (the JDBC driver class name)

$url (the database URL)

$user (username for database login)

$password (password for database login). This password string may optionally be
enclosed in parentheses to signify that it is encrypted. Where using encrypted
passwords, the command line parameter, -passKey, must be used when starting the
server to specify the key to be used to decipher.
[$database =>
[$app2Db =>
[$name => "Oracle App2 DB"
,$driver => "oracle.jdbc.OracleDriver"
,$url => "jdbc:oracle:thin:@acme:1213:app2"
,$user => "test"
,$password => "secret"
]
]
]
The second example below adds the set of keys which are each optional:

$schema (nominates a schema other than the default database schema)

$timeout (specifies login and/or query timeouts in seconds)

$pool (allows more control over the behavior of the JDBC connection pool)

$queryRows (specifies the maximum number of rows before warnings and errors are
reported from the lib/*AsVector methods)
[$database =>
[$app2Db =>
[$name => "Oracle App2 DB"
,$driver => "oracle.jdbc.OracleDriver"
,$url => "jdbc:oracle:thin:@acme:1213:app2"
,$user => "test"
,$password => "secret"
,$schema => "App2"
SQL Reference
487
,$timeout => [$login=>30, $query=>20]
,$pool => [$min=>1, $core=>3, $max=>10, $reduce=>[$inact=>120]]
,$queryRows => [$error => 2000, $warn => 500]
]
]
]
Where the application connects to more than one database, some configuration details
may be common. One set of details may be used as a template within another to avoid the
need to repeat information. Example:
[$database =>
[$mydefaults =>
[$timeout => [$login=>30, $query=>20]
,$driver => "oracle.jdbc.OracleDriver"
]
,$oracleDb =>
[$defaults => $mydefaults
,$name => " Oracle App2 DB"
,$url => "jdbc:oracle:thin:@acme:1213:app2"
,$user => "test"
,$password => "secret"
,$schema => "App2"
]
]
]
12.4 The Sql Pseudo Class
native sql.execQuery (native tagOrConnOrStmt, string sqlCmd = null)
SUMMARY:
This method is used to execute a database query, returning a result set.
One usage of this method is to pass a database connection and a string
containing dynamic sqlCmd. Under this usage, the required statement is
implicitly opened and closed. Where the result set should have some
special property, a statement object can be created prior to the call and
passed in as an alternative to the connection. It’s then the responsibility of
the programmer to close this statement.
Where executing a prepared or callable statement, this method is called
with just that one parameter. Immediately prior to executing the query,
this method internally sets all of the parameters within the statement.
EXAMPLE:
native rs1 = sql.execQuery(conn, "select now()");
native rs2 = sql.execQuery(prepStmt);
int sql.execUpdate (native tagOrConnOrStmt, string sqlCmd = null)
488
JavaGram Agile Development
This method is used to execute a database update, returning a count of
records affected.
One usage of this method is to pass a database connection and a string
containing dynamic sqlCmd.
Where executing a prepared or callable statement, this method is called
with just that one parameter. Prior to executing, this method internally sets
all of the parameters within the statement.
EXAMPLE:
int rc1 = sql.execUpdate(conn, insertSql);
int rc2 = sql.execUpdate(prepStmt);
int sql.exec (native stmt)
SUMMARY:
This method is more complex to use than other methods, but can handle
multiple result sets from callable statements. It’s called with just one
parameter – a prepared or callable statement. Prior to executing, this
method internally sets all of the parameters within the statement.
EXAMPLE:
sql.exec(stmt);
native sql.createStmt (vague tagOrConn, map opts = null)
SUMMARY:
Returns a new statement object.
Where opts is specified, the type, holdability, and concurrency of any
result sets created is further controlled. Possible values for these are the
same as those used for the text.sql.prepare element.
EXAMPLE:
native s = sql.createStmt($myDb);
native s2 = sql.createStmt($myDb, [$type => $forward_only,
$concurrency => $read_only,
$holdability => $close_cursors_at_commit]);
native sql.prepare (vague tagOrConn, string sqlCmd, map opts = null)
SUMMARY:
Often a text.sql.prepare element is the simplest choice for creating a
prepared statement. This method provides additional flexibility as it can
be used where the sqlCmd is dynamically formed.
Where opts is specified, the type, holdability, and concurrency of any
result sets created is further controlled. Possible values for these are the
same as those used for the text.sql.prepare element.
EXAMPLE:
string sqlCmd = "select * where surname = ? and firstname = ? and
dob = ?";
native ps = sql.prepare($myDb, sqlCmd);
native ps2 = sql.prepare($myDb, sqlCmd,
[$type => $forward_only,
$concurrency => $read_only,
$holdability => $close_cursors_at_commit]);
native sql.callable (vague tagOrConn, string sqlCmd, map opts = null)
SQL Reference
489
SUMMARY:
Often a text.sql.callable element is the simplest choice for creating a
callable statement. This method provides additional flexibility as it can be
used where the sqlCmd is dynamically formed.
Where opts is specified, the type, holdability, and concurrency of any
result sets created is further controlled. Possible values for these are the
same as those used for the text.sql.prepare element.
EXAMPLE:
string sqlCmd = "{call getCustomer(?)}";
native cs = sql.callable($myDb, sqlCmd);
native cs2 = sql.callable($myDb, sqlCmd,
[$type => $forward_only,
$concurrency => $read_only,
$holdability => $close_cursors_at_commit]);
native sql.set (native ps, int columnNum, vague value)
SUMMARY:
Sets the parameter values corresponding to question mark placeholders in
the original SQL of the prepared or callable statement.
EXAMPLE:
sql.set(ps, 1, "Smith");
sql.set(ps, 2, "Peter");
sql.set(ps, 3, [#1978-06-30]);
native sql.setBinFile (native ps, int columnNum, string filePath)
SUMMARY:
Similar to sql.set(), except that this is used for storing a binary file into a
binary/blob table column.
EXAMPLE:
sql.setBinFile(ps, 1, "Sample.gif");
native sql.getBinFile (native rs, int columnNum, string filePath)
SUMMARY:
Similar to sql.get(), except that this is used for retrieving a binary file
from a binary/blob table column.
EXAMPLE:
sql.getBinFile(rs, 1, "Sample.gif");
void sql.registerOutParam (native callableStmt, int column,
vague type = null, boolean alsoIn = false)
SUMMARY:
Registers the specified column as an output parameter to the callable
statement. The column is specified as a one-based column index. type is
one of the type codes from the Java class java.sql.Types (including
INTEGER, DATE and VARCHAR) or optionally specified as a Java class name.
type is case sensitive. alsoIn allows the parameter to be used both for
input and output (default is output only).
EXAMPLE:
sql.registerOutParam(cs, 3, "INTEGER", true);
sql.registerOutParam(cs, 4, "oracle.jdbc.OracleTypes.CURSOR");
boolean sql.next (native rs)
490
JavaGram Agile Development
SUMMARY:
Returns true if the result set contains an as-yet unseen row and moves to
that next row; otherwise, it returns false. In other words, the first call
returns true if one or more rows exist and positions to the first row.
EXAMPLE:
while (sql.next(rs)) { … }
boolean sql.previous (native rs)
SUMMARY:
Attempts to move the result set to the previous row, returning true if
successful and false otherwise.
EXAMPLE:
if (sql.previous(rs)) { … }
boolean sql.first (native rs)
SUMMARY:
Attempts to move the result set to the first row, returning true if successful
and false otherwise.
EXAMPLE:
while (sql.first(rs)) { … }
boolean sql.last (native rs)
SUMMARY:
Attempts to move the result set to the last row, returning true if successful
and false otherwise.
EXAMPLE:
while (sql.last(rs)) { … }
vague sql.nextResult (native prepStmt)
SUMMARY:
For prepared statements, this method steps through multiple result sets.
Each call may return a result set, an integer representing an update count,
or null when the results are exhausted. The number of result sets returned
is dependent on the query performed.
EXAMPLE:
while ((result = sql.nextResult(callable)) != null) {
if (result instanceof int)
sys.println(result);
else
while (sql.next(result))
sys.println(sql.getRow@map(result));
}
vague sql.get (native resultSetOrCallStmt, vague column)
SUMMARY:
Retrieves the specified column from the current row of the result set or
output parameters of a callable statement. The column may be given as a
symbol representing the column name or as a one-based column index.
EXAMPLE:
string s @= sql.get(rs, $surname);
int i @= sql.get(rs, 4); // column 4 is a database NUMBER(5)
vague sql.getRow (native rs)
SQL Reference
491
SUMMARY:
Returns a representation of the current row of the result set. By default,
this is a map where each entry maps a column name symbol to a
corresponding value whose type is automatically deduced from the
database column type.
Where a method cast is specified, an object of the required type is created,
and where column names and object field names match, the columns from
the result set are assigned to the object’s fields.
EXAMPLE:
vague m = sql.getRow(rs); // returns a map
Customer c = sql.getRow@Customer(rs);
void sql.update (native rs, vague column, vague newValue)
SUMMARY:
In preparation for updating the current row of the result set, this method
allows modification of the value of one column. A series of calls to alter
different columns may be made. The column may be specified as a
symbol representing the column name or as a one-based column index.
EXAMPLE:
sql.update(rs, $height, 123.456789);
void sql.updateRow (native rs)
SUMMARY:
Commits to the database the changes to the result set row earlier made
using sql.update().
EXAMPLE:
sql.updateRow(rs);
native sql.getGeneratedKeys (native stmt)
SUMMARY:
Returns a result set containing the values of all of the auto generated keys
created at the last insert on this statement.
EXAMPLE:
native rs = sql.getGeneratedKeys(stmt);
void sql.setMaxRows (native stmt, int rows)
SUMMARY:
Sets a limit on the number of rows in any result set created by executing
this statement. Any rows in excess of this limit are silently dropped.
EXAMPLE:
sql.setMaxRows(stmt, 50);
int sql.getQueryRowsError (native tagOrConnOrStmt)
SUMMARY:
Gets the limit on the number of rows retrieved from a result set before an
error is thrown.
By default, this value is 0, indicating an infinite number of rows may be
handled before an error of this type is raised.
EXAMPLE:
int rows = sql.getQueryRowsError($myDb);
void sql.setQueryRowsError (native tagOrConnOrStmt, int rows)
SUMMARY:
Sets the limit on the number of rows retrieved from a result set before an
error is thrown. This limit can be used to prevent programming errors that
lead to enormous memory allocations.
EXAMPLE:
492
sql.setQueryRowsError($myDb, 2000);
JavaGram Agile Development
int sql.getQueryRowsWarn (native tagOrConnOrStmt)
SUMMARY:
Gets the limit on the number of rows retrieved from a result set before
warning messages are logged.
By default, this value is 0, indicating an infinite number of rows may be
handled before a warning of this type is given.
EXAMPLE:
int rows = sql.getQueryRowsWarn($myDb);
void sql.setQueryRowsWarn (native tagOrConnOrStmt, int rows)
SUMMARY:
Sets the limit on the number of rows retrieved from a result set before
warning messages are logged. This limit can be used to prevent
programming errors that lead to enormous memory allocations.
EXAMPLE:
sql.setQueryRowsError($myDb, 200);
string sql.getCursorName (native rs)
SUMMARY:
Returns the name of the SQL cursor underlying this result set.
EXAMPLE:
string name = sql.getCursorName(rs);
int sql.getColumnCount (native rs)
SUMMARY:
Returns the number of columns within this result set.
EXAMPLE:
int c = sql.getColumnCount(rs);
vague sql.getColumnMetaProperty (native rs, int column, symbol property)
SQL Reference
493
SUMMARY:
Gets a metadata value for a property of a column (specified using its onebased index). Valid properties include:
Property
EXAMPLE:
Type
Sample Value
$autoIncrement
boolean
true
$caseSensitive
boolean
false
$catalogName
string
null
$columnClassName
string
$java.lang.Long
$columnDisplaySize
int
10
$columnLabel
string
$ID
$columnName
string
$ID
$columnType
int
4
$columnTypeName
string
${INTEGER UNSIGNED}
$currency
boolean
false
$definitelyWritable boolean
true
$nullable
int
0
$precision
int
10
$readOnly
boolean
false
$searchable
boolean
true
$scale
int
0
$schemaName
string
null
$signed
boolean
false
$tableName
string
$ORDERS
$writable
boolean
true
vague cn = sql.getColumnMetaProperty(rs, i, $columnName);
native sql.loadDriver (string classPath , int timeout = 0)
SUMMARY:
Loads the JDBC driver(s) found in the specified classPath. The login
timeout is specified in seconds, (defaults to zero, meaning no timeout).
This method and sql.connect() are not normally required because query
and transaction blocks, together with configuration information
automatically take care of loading the driver and obtaining a connection.
EXAMPLE:
sql.loadDriver("com.mysql.jdbc.Driver");
native sql.connect (symbol configSym)
native sql.connect (string url, string user, string password)
SUMMARY:
Returns a connection to a database. The preferred usage of this method is
to pass a symbol referring to a section in the application’s configuration
file.
This method is best used only in test programs because the query and
transaction blocks provide equivalent functionality in a more convenient
and robust form.
EXAMPLE:
494
native conn = sql.connect($oracleDb);
native conn = sql.connect("jdbc:mysql://server2/db_c", "app",
"password1");
JavaGram Agile Development
void sql.close (native dbResource)
SUMMARY:
Closes a database statement, result set, or connection.
Many developers will never need to call this as it’s not required when the
database resource is created in a query or a transaction block, as these
automatically close any resources created. May be used to close resources
before the block end is encountered.
EXAMPLE:
native conn = sql.connect("jdbc:mysql://server2/db_c", "app",
"password1");
sql.close(conn);
12.5 Business Object Manager
The Business Object Model (bom) is a persistence layer that simplifies the storage and
retrieval of JavaGram application data held in a database. This is achieved by subclassing
lib/bom/Object and using its methods, which include persist() and find(). To enable
these simple interfaces, you need to specify the mapping between a database table and the
corresponding JavaGram class. Example:
<jag>
<load>"lib/bom/Object"</load>
<load>"lib/bom/UserLockableObject"</load>
class Customer extends UserLockableObject {
int myid;
string lastname, givenname, email, hphone, mphone, path, type;
static {
defineTable(Customer,
[$db => $mysqlDb2, //$oracleXeDb,
$table => $CUSTOMER,
$columns => [
$id => $myid,
// long form, identical to $surname => $lastname:
$surname => [$fields => $lastname],
// implicit, not required:
//$givenname => $givenname,
$rest => [$fields => [$email, $hphone, $mphone]],
$contract_image => [$fields => $path, $viaFile => $binary, $extend => true]
],
$lock => [$column => $locked_by]
]);
}
public slocal static <text.sql.update int createTable () db=$mysqlDb2>
create table if not exists customer (
id integer not null auto_increment,
surname varchar(32) not null,
givenname varchar(32) not null,
contract_image mediumblob,
SQL Reference
495
rest text,
locked_by integer,
primary key (id)
)
</text.sql>
…
}
Recognized tags within the database table definitions are shown below in bold.
Tag
Example Usage
$db
$table
$columns
$myOracleDb
$ACCOUNT
$surname => $lastname
$surname => [$fields =>
$lastname]
$other => [$fields =>
[$email, $phone]]
$other => [$fields =>
[$email, $phone], $extend
=> true]
$other => [$fields =>
[$email, $phone],
$compress => true]
$image => [$fields =>
$path, $viaFile =>
$binary]
$lock
496
[$column => $locked_by]
Description
Database tag from Jag configuration.
Database table name.
Set of mappings from database column name to
Jag field name.
Causes the database column surname to map to
the member variable lastname.
Long form, with the same meaning as the
previous example. This form allows the
introduction of additional qualifiers.
Causes the email and phone member variables
to be automatically serialized and de-serialized
together on placement and retrieval from the
database column other.
The addition of $extend causes the database
column $other to be regarded as extended
information, that is not inserted, updated or
selected by default. To persist (or retrieve) an
extended field, setExtendOn(true) should be
called on the object.
The addition of $compress causes the data to be
automatically compressed before insertion into
the database and decompressed on retrieval.
On insert/update, the $viaFile clause causes
content to be read from the file named by
$path. Thus, $path might be set to
“/temp/portrait6602.jpg” by the programmer
prior to insert. On select, as each row is
retrieved, Object.getViaFilePath() is called,
allowing the programmer to set a filename the
data should be read into. The value of $viaFile
may be $binary or $char.
The $lock directive allows this table to be
locked with the locking user id stored in the
$locked_by column.
JavaGram Agile Development
12.6 The Bom Pseudo Class
The bom pseudo class underlies the lib/bom/Object class. Most users will use the facilities
of lib/bom/Object and have no need to make direct use of the methods described here.
Therefore, the descriptions here are of interest mainly in assisting understanding of the
lib/bom/Object class.
vector<vague> bom.find (vague klass, vague criteriaOrField, vague value = null)
SUMMARY:
This method uses the database mappings for klass to search the database
table for matching records and return a vector of objects of type klass.
When criteriaOrField is a map, its conditions are matched against the
database rows that are to be included in the returned vector. Possible map
keys are $exact or $like, either of which should denote a map of object
field symbols (that correspond to some table column) against their desired
value. When criteriaOrField is null or empty or its $exact and $like
entries are both empty, a wildcard search is performed. When
criteriaOrField is a symbol, all rows whose nominated field match value
are returned (this is similar to bom.findOne() except that a vector of
matching objects is returned).
EXAMPLE:
vector<Customer> vec @= bom.find(Customer,
[$exact => [$givenname => "Bob", ...],
$like => [$lastname => "Sm%", ...]]
);
Object bom.findOne (vague klass, symbol field, vague value)
SUMMARY:
This method uses the database mappings for klass, together with an object
field name and required value. The first matching row (if any) is
returned. Otherwise null is returned.
EXAMPLE:
Customer cust @= bom.findOne(Customer, $id, 100);
vague bom.getId (Object object)
SUMMARY:
This method returns the unique identifier (primary key) of object.
EXAMPLE:
vague id = bom.getId(this);
void bom.persist (Object object)
SQL Reference
497
SUMMARY:
This method saves object to the underlying database. A database update is
performed if this object originated from a bom.find() call or if there has
been a previous bom.persist() call. Otherwise a database insert is
performed.
Where an insert is being performed, bom.persist() calls
Object.handlePrimaryKey(), giving the application class the opportunity
to set the database primary key. This method may either set the primary
key and return true or simply return false, signaling that the database will
automatically assign a primary key as the object is inserted.
EXAMPLE:
bom.persist(this);
void bom.delete (Object object)
SUMMARY:
This method deletes object from the underlying database.
EXAMPLE:
bom.delete(this);
void bom.refresh (Object object)
SUMMARY:
This method reads object from the underlying database again,
synchronizing to the latest database content, discarding any in-memory
changes.
EXAMPLE:
bom.refresh(this);
void bom.lock (Object object, vague byUserId)
SUMMARY:
This method attempts to assert a lock at the database level. An exception
is thrown if this operation fails.
The exact action taken depends on the underlying locking mechanism
chosen for this business object. In the default locking model, the database
record for this object is updated with the supplied user ID stored in a lock
column.
EXAMPLE:
bom.lock(this, "tom.maxwell");
void bom.unlock (Object object, vague byUserId)
SUMMARY:
This method removes a lock previously asserted at the database level. An
exception is thrown if this operation fails.
The exact action taken depends on the underlying locking mechanism
chosen for this business object. In the default locking model, the database
record for this object is updated to remove the supplied user ID stored in
the lock column, leaving the database record in its original state.
EXAMPLE:
bom.unlock(this, "tom.maxwell");
vague bom.lockedBy (Object object)
SUMMARY:
This method queries the database for the current locker of object. Either
the current locker or null is returned to indicate that the record is not
currently locked.
EXAMPLE:
498
vague locker = bom.lockedBy(this);
JavaGram Agile Development
SQL Reference
499
13
Library Reference
This chapter describes the standard library scripts that are bundled with every JavaGram
release. These scripts provide a variety of classes that make JavaGram programming
easier and more productive. They also promote design patterns that represent best
practice. Use them wherever appropriate in your projects. The scripts are organized by
domain names that mirror their directory structure, and are illustrated here as UML
diagrams.
13.1 Lib/lang/
This directory contains scripts that provide ‘linguistic’ support.
Lib/lang/Clipboard.jag contains the singleton Clipboard class which provides a generic
way of storing and retrieving clipboard data (indexed via symbols) for your applications.
Lib/lang/Common.jag contains a number of classes for performing common tasks in
applications:

Util provides a set of static members for memory, file, and string handling.

Desktop provides methods for opening, editing, and printing files.

Thread serves as the base class of application-defined threads. Simply subclass it and
implement its abstract run() method.

ThreadLocal provides a way of defining static variables that are local to threads.

Timer provides a mechanism for defining non-GUI timers, so it’s especially suitable
for servers. Simply instantiate it and schedule timer tasks using its schedule()
method.

TimerTask serves as the base class of all timer tasks to be scheduled with a Timer.
Simply subclass it and implement its run() method.

Process provides a mechanism for creating and handling processes. Simply instantiate
it and call its exec() method.

Callback serves as a base class for defining anonymous callback for asynchronous
calls.
Lib/lang/Event.jag contains classes that you would subclass for the purpose of
application-level event handling:

500
Event serves as the base class of all application events.
JavaGram Agile Development

EventInitiator serves as a mutual base class of any class that raises application
events. To register an event listener, call the addListener() method. To raise an event,
call the raiseEvent() method.

EventListener serves as a mutual base class of any class that listens to application
events. Simply subclass it and implement its consumeEvent() method.
Lib/lang/Format.jag contains the Format class for formatting typed data.
Lib/lang/Interfaces.jag contains the Comparable and Cloneable interfaces.
Lib/lang/ML.jag contains classes for multi-lingual support.
Library Reference
501
13.2 Lib/svr/
This directory contains scripts for deploying servers.
To deploy a server, nominate one of these as the main script.
AppServer.jag defines AppServer whose main() method boots a generic application
server.
ProxyServer.jag defines ProxyServer whose main() method boots a generic proxy server.
502
JavaGram Agile Development
BlazeServer.jag defines BlazeServer whose main() method boots a BlazeDS application
server for deployment into a servlet container such as Tomcat.
13.3 Lib/io/
This directory contains scripts for performing input/output operations.
Cookie.jag provides access to browser cookies.
DateParser.jag supports parsing of dates expressed in many different formats.
Export.jag enables you to export application data in JAG or XML format.
FTPClient.jag provides FTP client functionality for accessing an FTP server.
Library Reference
503
IOUtil.jag provides useful IO utility methods.
Matkup.jag provides methods for the transformation of markup data (e.g., HTML).
Report.jag defines classes that provide template-driven report generation capability.
Mf3270.jag defines a class of the same name for mainframe screen scraping.
Soap.jag provides classes for creating and sending SOAP messages.
XML.jag provides classes for processing XML data.
13.4 Lib/gui/
This directory contains a variety of scripts for defining and working with GUI elements.
For ease of description, they’ve been divided into a number of logical categories below.
GuiUtil
Upgrade
#w ait: boolean = true
#mjvMajor: int
#mjvMinor: int
#remotePath: string
#localPath: string
#s: stream
-app: <App>
+check(mjvMajor:int, mjvMinor:int, remotePath:string, localPath:string, s:stream): void
#proceed(): void
#frameHandler(comp:native, event:symbol): void
#getRebootCmd(jarPath:string): string
#exit(code:int): void
+TICK_STR: string = "\u2713"
+DISABLE_COLOR: string = "0x999999"
+DATE_FORMAT: string = "dd MMM yyyy"
+DATE_TIME_FORMAT: string = "dd MMM yyyy, hh:mm:ss"
#mainFrame: native
+blankIcon: <Icon>
+openFolderIcon: <Icon>
+closedFolderIcon: <Icon>
+addComp(container:native, comp:native, lay:symbol): void
+setMainFrame(frame:native): void
+getMainFrame(): native
HtmlGenerator
PickList
PickListMgr
#namedCombos: map<symbol,vector<native>> = map()
#modified: boolean
#itemEdited: boolean
#lastSelIdx: int
-dialog: <Dialog>
+getList(name:symbol): PickList
+registerList(name:symbol, combo:native): vector<string>
+editList(name:symbol, visual:boolean): void
+refreshLists(): void
+show (pickList:PickList): void
#fieldHandler(comp:native, event:symbol): void
#listModel(comp:native, cmd:symbol, idx:int): vague
#listHandler(comp:native, event:symbol): void
#applyItemCmd(): void
#cancelItemCmd(): void
#new Cmd(): void
#canDelete(): boolean
#deleteCmd(): void
#canMoveUp(): boolean
#moveUpCmd(): void
#canMoveDow n(): boolean
#moveDow nCmd(): void
#okPressed(): void
#cancelPressed(): void
#setModified(mod:boolean): void
504
pickList
+generateCustom(): Document
+generateAuto(path:string, screen:Screen, title:string): void
+generateDirect(path:string, data:string, title:string): void
-_generateAux(path:string, src:vague, title:string): void
+textToHtml(text:string): string
+htmlHeader(title:string): <text>string
+htmlTrailer(): <text>string
Screen
InfoBar
-w heelIdx: int = 0
-READY_GIF: string = "lib/gifs/Ready.gif"
-LOCK_GIF: string = "lib/gifs/Lock.gif"
-UNLOCK_GIF: string = "lib/gifs/Unlock.gif"
-WHEEL_GIFS: vector<string> = [...]
-view : <Panel>
-timer: <Timer>
HtmlScreen
-urlPrefix: string
-view : <Panel>
+getView (): native
+activate(): native
+startTask(name:string): void
+endTask(): void
#formatInfo(): string
+updateMemory(): void
#gc(): void
+setSecurity(secure:boolean): void
#timerHandler(comp:native, event:symbol): void
+HtmlScreen(urlPrefix:string)
#anchorHandler(comp:native, url:string): void
#urlKind(url:string): symbol
#doHttpRef(url:string): void
#doExprRef(refStr:string): void
#doLocalRef(path:string): void
#doFileRef(path:string, isLocal:boolean): void
#getRefFiles(path:string, local:boolean): string
+bind(content:Object): void
+setFile(path:string, local:boolean): void
JavaGram Agile Development
GuiUtil.jag contains the GuiUtil class which defines a number of useful static members.
The setMainFrame() and getMainFrame() methods are the most-frequently ones, providing
a convenient way of registering and accessing an application’s main frame.
Upgrade.jag contains the Upgrade class which implements an automatic method for
upgrading JAG.jar according to an application-specified ‘minimum required version’.
InfoBar.jag contains the singleton InfoBar class which implements a generic information
bar for display at the bottom of an application. It displays progress (for downloads and
remote calls), memory usage, and security information.
PickListMgr.jag contains the singleton PickListMgr class for managing pick-lists (lists
displayed for customizable combo boxes).
Html.jag contains classes for generating and viewing HTML reports:

HtmlGenerator serves as a mutual base class of any screen that requires HTML report
generation capability.

HtmlScreen provides a screen for viewing HTML content. You can use it ‘as is’ or
subclass it and add features.
Library Reference
505
EventInitiator
EventListener
ShowContentEvent
+content: Object
+Show ContentEvent(screen:Screen, content:Object)
NavTree
#lastSelNode: native
#navChain: vector = vector()
#navIndex: int = -1
#ignoreChain: boolean = false
#selFirstTab: boolean = false
#modNodes: vector = vector()
#tree: <Tree>
-saveBeforeExitAlert: <Alert.ync>
+NavTree(app:GuiApp)
+getTree(): native
#onShowContent(stage:symbol): void
+reload(): void
#createNode(parent:native, title:string, icon:native, presentation:Screen, content:Object): vague
+selectNode(node:symbol): void
#selectParentNode(node:vague): void
+getContent(node:vague): Object
+setContent(node:vague, content:Object): void
+findChildNode(parent:vague, w ithContent:Object): vague
#getScreen(node:vague): Screen
#treeHandler(comp:native, event:symbol): void
#onDrillNode(node:native): void
#setModified(): void
#maintainModNodes(node:vague, modified:boolean): void
+persistNodeCmd(maintain:boolean): boolean
+persistAllNodesCmd(maintain:boolean): boolean
#persistNode(node:vague): void
+unlockModifiedNodes(nodes:vector): void
+unlockNodeCmd(maintain:boolean): void
+hideNodeCmd(maintain:boolean): boolean
+hideNode(node:native, check:boolean, selectSibling:boolean): void
+hideChildNode(parent:native, w ithContent:vague, selectSibling:boolean): void
+hideChildren(node:native): boolean
#hasModified(nodes:vector): boolean
+deleteNodeCmd(maintain:boolean): boolean
+reloadNodeCmd(maintain:boolean): boolean
+goBackCmd(maintain:boolean): boolean
+goForw ardCmd(maintain:boolean): boolean
#updateNavChain(node:vague): void
#removeFromNavChain(node:vague): void
+show NodeContentCmd(maintain:boolean, syntax:symbol): boolean
+consumeEvent(event:Event): boolean
+canExit(): boolean
Event
ShowNoContentEvent
GuiApp
-app: native
app
+GuiApp(frame:native)
+getNativeApp(): native
+run(): void
+exit(): void
+beginLengthy(): void
+endLengthy(): void
+getUserId(): vague
GuiApp.jag contains the GuiApp class which serves as the base class of all GUI
applications. To implement a GUI application, subclass it and call its run() method.
NavTree.jag contains classes that support navigation trees:

NavTree serves as the base class of a navigation tree. It provides a rich set of methods
for managing tree nodes, binding, and persistence.

ShowContentEvent is used by NavTree to tell the rest of the application that a selected
node’s ‘content’ should be displayed.

ShowNoContentEvent is used by NavTree to tell the rest of the application that a selected
node has no ‘content’.
506
JavaGram Agile Development
parent
EventInitiator
Event
Object
content
Screen
content
#content: Object
#editable: boolean = true
#autoApply: boolean = true
#ignoreEdits: boolean = false
#defFocusElem: native
#modified: boolean = false
#isBinding: boolean = false
#Hot: <Plate>
+Screen()
+Screen(parent:Screen)
+getView (): native
+maintain(): void
+clear(): void
+bind(content:Object): void
#bindView (content:Object): void
+save(): void
#saveView (): void
+persist(): void
+reflectDeletedObj(obj:Object): void
#onModified(): boolean
#onBind(stage:symbol): void
#onSave(stage:symbol): void
#hotEnable(): boolean
#hotHandler(comp:native, event:symbol): void
+toHtml(buffer:stream, indent:int): void
#indentHtml(buffer:stream, indent:int): void
ShowContentEvent
ScreenModifiedEvent
screen
+WizardCard(w izard:Wizard, panel:native)
+getView (): native
+getName(): symbol
+getBackName(): symbol
+getNextName(): symbol
+canFinish(): boolean
+onShow (show :boolean): void
+ScreenModifiedEvent(screen:Screen)
HtmlScreen
screen
ScreenContainer
#parts: vector<Screen> = vector()
+ScreenContainer(parent:Screen, parts:vector<<Screen>)
+setParts(parts:vector<<Screen>): void
+addPart(part:Screen): void
#bindView (content:Object): void
#saveView (): void
#onBind(stage:symbol): void
#onSave(stage:symbol): void
#tabsHandler(comp:native, event:symbol): void
EventListener
Wizard
WizardCard
#panel: native
#name: symbol
ShowNoContentEvent
currCard
firstCard
+BACK_TITLE: string = "Back"
+NEXT_TITLE: string = "Next"
+FINISH_TITLE: string = "Finish"
+CANCEL_TITLE: string = "Cancel"
#cardMap: map<symbol,WizardCard> = map()
#firstCard: WizardCard
#currCard: WizardCard
-w izard: <Dialog>
+Wizard(title:string, w idth:int, height:int, modal:boolean)
+getDialog(): native
+addCard(card:WizardCard): void
+show (b:boolean): void
+show Card(card:WizardCard): void
+show FirstCard(): void
#maintainButtons(): void
-backPressed(): void
-nextPressed(): void
-finishPressed(): void
-cancelPressed(): void
#onFinish(): void
#onCancel(): void
+consumeEvent(event:Event): boolean
+onModified(): void
Screen.jag contains classes for managing generic screens:

Screen serves as the base class of any screen that requires binding and persistence.
The ‘content’ of a screen is represented by an Object. Screens may be nested – each
screen keeps track of its parent for the purpose of ‘propagating’ actions.

ScreenModifiedEvent is used by Screen to notify the rest of the application that a
screen’s ‘content’ has been modified.
Library Reference
507
ScreenContainer.jag contains the ScreenContainer class which can be used to group a
set of screens together to form a larger one.
Wizard.jag contains classes for defining wizard style dialogs:

Wizard sequences a set of WizardCard instances together to form a wizard, allowing
the user to move from one card to the next/previous card, to enter data, or make
selections. The cards are linked dynamically.

WizardCard serves as the base class of any screen that’s to be used a wizard card.
Table
Screen
#tableData: vector = []
#tableFormat: vector<map> = []
#lastSelRow s: vague
#table: <Table>
+Table(tableFormat:vector<<map>, itemScreen:Screen, parent:Screen)
+getTable(): native
+getTableData(): vector
+setTableData(tableData:vector): void
+getColFormatMap(row :int): map
+isEmpty(): boolean
+isSorted(): boolean
+hasSelection(): boolean
+countSelected(): int
+selectAll(): void
+getSelectedRow (): int
+selectRow (row :int): void
+getSelectedItem(): vague
+getSelectedItems(): vector
+appendRow (row Data:vague): void
#tableModel(comp:native, cmd:symbol, row :int, col:int): vague
#getCell(row :int, col:int): vague
#formatCell(obj:vague, key:symbol): vague
#updateCell(comp:native, item:vague, key:symbol, new Val:vague): void
#validateCell(comp:native, item:vague, key:symbol): boolean
#cellStyle(row :int, col:int): vague
#cellIcon(row :int, col:int): vague
#sort(col:int, ascend:boolean): void
#tableHandler(comp:native, event:symbol): void
#row ToObject(row :vague): Object
#onSelectRow (): void
#applyImplicit(): void
+clear(): void
+refresh(): void
+detailsCmd(maintain:boolean): boolean
#drillRow (bo:Object): Object
+new Cmd(maintain:boolean): boolean
#createRow (): vague
+applyCmd(maintain:boolean): boolean
+deleteCmd(maintain:boolean): boolean
#deleteRow (idx:int): vague
+cutCmd(maintain:boolean): boolean
+copyCmd(maintain:boolean): boolean
+pasteCmd(maintain:boolean): boolean
+findMatching(prefix:string): int
+expandRow (row :int, refresh:boolean): void
+collapseRow (row :int, refresh:boolean): void
TableButtons
itemScreen
+ALL_BUTNS: vector<symbol> = [details,new ,delete,jump]
#buttons: vector<symbol>
+view : <Panel>
+TableButtons(table:Table, buttons:vector<<symbol>)
+TableButtons(buttons:vector<<symbol>)
+setTable(table:Table): void
+clearJump(): void
#jumpHandler(comp:native, event:symbol): void
EmbedTickTable
#tickKey: symbol
#tickCol: int
-tickEditor: <Option.tick>
+EmbedTickTable(tableFormat:vector<<map>, tickKey:symbol, parent:Screen)
#reformat(formats:vector<<map>, tickKey:symbol): vector<map>
#tableHandler(comp:native, event:symbol): void
#enabled(row :int): boolean
#toggle(row :int): boolean
#cellStyle(row :int, col:int): vague
+doAll(action:symbol): void
#_doAll(action:symbol): void
+countTicked(): int
+getTickedRow s(): vector<int>
+getTickedData(key:symbol): vector
TickTable
#ticks: vector<boolean> = vector()
+TickTable(tableFormat:vector<<map>, tickKey:symbol, parent:Screen)
#getCell(row :int, col:int): vague
-syncTickVec(): void
#toggle(row :int): boolean
#_doAll(action:symbol): void
+countTicked(): int
+getTickedRow s(): vector<int>
+getTickedData(key:symbol): vector
Table.jag contains classes for defining and working with tables:

Table serves as the base class of all tables that require binding. It provides methods
for drilling into rows, adding new rows, and clipboard operations on rows.

TableButtons provides a set of standard table button for drilling, adding, and deleting
rows. The action handlers for these buttons are linked to the corresponding table
methods.
508
JavaGram Agile Development
TickTable.jag contains classes that support ‘tickable’ tables (i.e., a table where a column
is nominated to contain tick boxes):

EmbedTickTable serves as the base class for a tickable table where the tick data is
embedded within the table data. If table format has a column that specifies the tick
key then it’s used. Otherwise, one is created in position 0. If each row is an object,
then the object must have a tick key as a field. Otherwise, toggling the tick is not
allowed. If each row is a map, then the tick key is added dynamically when needed.

TickTable serves as the base class for a tickable table where the tick data is kept
separate from the table data. The tick key can be null, in which case the table format
should not define the tick column.
Library Reference
509
SearchCriteriaPanel
#search: SearchScreen
#criteria: map = map()
#Hot: <Plate>
-view : <Panel>
+SearchCriteriaPanel(search:SearchScreen, criteriaPanel:native)
+getCriteria(): map
+save(): void
+clear(): void
#hotHandler(comp:native, event:symbol): void
criteriaPanel
Screen
search
SearchScreen
-TICK_DATA: vector<string> = ["","Yes","No"]
-view : <Panel>
+SearchScreen(criteria:SearchCriteriaPanel, result:SearchResultScreen, parent:Screen)
+findCmd(): void
+canFind(): boolean
#find(criteria:map): vector
+clearCmd(): void
+canClear(): boolean
+reflectDeletedObj(obj:Object): void
search
resultScreen
Table
table
TableButtons
tableButns
SearchResultScreen
#search: SearchScreen
#tableButns: TableButtons
#DEFAULT_TITLE: string = "Search Result"
-view : <Panel>
+SearchResultScreen(search:SearchScreen, tableFormat:vector<<map>, tableButns:TableButtons)
+setResult(result:vector): void
+clear(): void
+reflectDeletedObj(obj:vague): void
#setTitle(title:string): void
TableAndPieSearchResultScreen
#PIE_COLORS: vector<string> = [...]
#pieKeys: vector<symbol>
#pieVec: vector<map> = vector()
-view : <Pane.tabbed>
+TableAndPieSearchResultScreen(search:SearchScreen, tableFormat:vector<<map>, tableButns:TableButtons,
+setResult(result:vector): void
+clear(): void
#setTitle(title:string): void
#pieComboModel(combo:native, cmd:symbol, idx:int): vague
#pieKeyToColIdx(key:symbol): int
#pieComboHandler(comp:native, event:symbol): void
+rebuild(): void
#buildPieData(): void
#cycleColor(idx:int): string
#pieModel(combo:native, cmd:symbol, idx:int): vague
Search.jag contains classes that support customizable search user interface behavior:

SearchCriteriaPanel serves as the base class of a search criteria panel, with find and
clear buttons.
510
JavaGram Agile Development

SearchScreen serves as the abstract base class of a search screen. Subclass it and
implement its find() method. The constructor expects a SearchCriteriaPanel and a
SearchResultScreen.

SearchResultScreen serves as the base class of a screen that displays search results.
This is typically subclassed anonymously when invoking the SearchScreen
constructor.

TableAndPieSearchResultScreen is a variant of SearchResultScreen that displays the
search result as a tabbed pane, where the first tab displays the result as a table and the
second tab displays it as a pie chart.
13.5 Lib/bom/
This directory contains scripts that support Business Objects (BOs).
Library Reference
511
Object
-_tableDef: map<symbol,map> = map()
-_persisted: boolean = false
ThreadLocal
extendOn
PickListDetails
-id: int
-editable: boolean
-name: string
-description: string
-items: vector<string>
#Object()
#Object(dbTableDef:map)
+defineTable(objKlass:vague, dbTableDef:map): void
+getId(): vague
+title(): string
+persisted(): boolean
+isExtendOn(): boolean
+isExtendOn(klass:symbol): boolean
+setExtendOn(value:boolean): void
+setExtendOn(klass:symbol, value:boolean): void
+findOne(klass:vague, field:symbol, value:vague): Object
+find(klass:vague, field:symbol, value:vague): vector<Object>
+find(klass:vague, criteria:map): vector<Object>
+onModified(): void
+persist(): void
#handlePrimaryKey(): boolean
#getViaFilePath(dbColumn:symbol, field:symbol, row Num:int): string
+delete(): void
#_delete(): void
+refresh(): void
+isLockable(): boolean
#isLockable(tableDef:map): boolean
#getTableDef(): map
#getTableDef(klass:symbol): map
#getDbTag(): symbol
#getDbTag(tableDef:map): symbol
#getDbTable(): symbol
#getDbTable(tableDef:map): symbol
+dropTable(klass:vague, db:symbol): void
PickLists
PickList
+createTable(): <text.sql.update>int
+PickList(details:PickListDetails)
+title(): string
+getId(): vague
+countItems(): int
+getItem(idx:int): string
+setItem(idx:int, item:string): void
+sw apItems(idx1:int, idx2:int): void
+appendItem(item:string): void
+deleteItem(idx:int): void
-findById(id:vague): PickList
-findByName(name:string): PickList
-findAll(): vector<PickList>
Lockable
+isLockable(): boolean
+lock(tag:vague): void
+unlock(tag:vague): void
Document
#plVec: vector<PickList>
#plMap: map<string,PickList>
+PickLists()
+getByName(name:string): PickList
+getAll(): vector<PickList>
+clearCache(): void
UserLockableObject
#path: string
#name: string
#isLocal: boolean
#pathMap: map<string,Document> = map()
+Document(path:string, isLocal:boolean)
+title(): string
+getId(): vague
+delete(): void
+getByPath(path:string): Document
+view (): void
-_lockedByUser: vague = null
#UserLockableObject()
+lock(byUserId:vague): void
#_lock(byUserId:vague): void
+unlock(byUserId:vague): void
#_unlock(byUserId:vague): void
+lockedBy(): vague
+lockedBy(probable:boolean): vague
#_lockedBy(): vague
Exception
CantLockException
+CantLockException(reason:string)
OutOfDateException
CantUnlockException
+CantUnlockException(reason:string)
Object.jag defines the abstract mutual Object class that serves as the base class of all
BOs. It supports persistence and database search functionality via the bom pseudo class.
Lockable.jag defines classes for implementing locking functionality for BOs:

Lockable is the abstract mutual base class (interface) for any BO that needs to be
lockable.

CantLockException is raised when a BO can’t be locked.

CantUnlockException is raised when a BO can’t be unlocked.

OutOfDateException is raised when a lockable BO is found to be older than what’s in
the database.
512
JavaGram Agile Development
UserLockableObject.jag defines the UserLockableObject mutual class which is a variant
of Object that has built-in support for locking by users.
Document.jag defines the Document class which represents disk-based documents.
PickList.jag contains classes for managing pick lists:

PickListDetails captures pick list data.

PickList defines a pick list as a persist-able object.

PickLists represents all pick lists in an application.
13.6 Lib/sql/
SqlUtil.jag defines the SqlUtil class which provides helper methods for SQL
programming.
Exception
TooManyRowsException
-TooManyRow sException(limit:int)
SqlUtil
+queryAsVector(connOrStmt:vague, q:string, klass:vague): vector
+prepStmtAsVector(prepStmt:native, klass:vague): vector
+resultSetAsVector(resultSet:native, klass:vague): vector
-log(msg:string): void
-w arn(row s:int, w arn:int, complete:boolean): void
Library Reference
513
14
JADE – JavaGram IDE
JADE is the Integrated Development Environment (IDE) for JavaGram, making
JavaGram programming much easier and more productive than resorting to plain text
editors and command lines. JADE allows you to organize your work as a project, define
project components, visually edit color-coded scripts, define run configurations, debug
your code, and a variety of other tasks. JADE automatically builds your project in the
background as you develop your code, so that you always have an up-to-date view.
14.1 Overview
The visual structure of JADE is shown below (with the Quest sample project loaded).
A menu bar and a toolbar at the top of the frame provide access to JADE’s commands.
The rest of the frame is split into three panes:

The top left pane displays the project navigation tree. The root of this tree points to
the project root directory.

The top right pane displays files that are currently open for editing.
514
JavaGram Agile Development

The bottom pane (task pane) displays various tabs for visual feedback, as well as the
run/debug tabs.
JADE’s frame can be viewed in one of three configurations:

Press the Full Project button in the main toolbar or choose the same from the View
menu to view all three panes. This is the default view.

Press the Project and Editor Only button in the main toolbar or choose the same from
the View menu to hide the task pane.

Press the Editor Only button in the main toolbar or choose the same from the View
menu to maximize the editor’s real estate.
The status bar at the very bottom of JADE’s frame provides real-time feedback. On its
left, you see feedback from JADE’s incremental build process. For example, when you
open a project, you’ll notice that its parsing and analysis progress is shown there. The
next segment shows the read/write status of the current file and its line count. The next
segment shows the line and column number of the cursor within the editor. The last
segment shows the amount of memory used versus allocated, and a trashcan button
which, when pressed, forces garbage collection (you don’t really need to do this, as
garbage collection is performed automatically).
14.2 Projects
JADE requires that you organize your work in projects. A project’s definition is stored in
a file with ‘.jade’ extension (e.g., Quest.jade). Only one project can be loaded at a time.
The name of this project appears in JADE’s frame title, followed by the full path of the
front-most script open in the editor.
14.2.1 Project Operations
To create a new project, select File|New, or press the Create New File button in the
toolbar. The following wizard dialog appears.
Select JADE Project from the tree and press the Next button.
JADE – JavaGram IDE
515
To specify the project folder, press the Project Folder button and browse to the desired
directory, or simply type the path in the field next to it. Once you’ve entered a name in
the Project Name field, the Finish button is enabled. Press this button and your project
will be created, ready for use.
To load an existing project, select File|Open Project, browse to the desired project file
and open it.
To save your changes to a project, select File|Save Project. Alternatively, you can press
the Save All button in the main toolbar, which causes the project as well as any modified
files open in the editor to be saved.
To close a project, click the cross in project tree tab, or choose File|Close Project. If the
project has any unsaved changes and/or any of its open files have unsaved changes,
you’ll be asked as to whether you want to save the changes.
For convenience, JADE keeps a list of the last few opened projects and displays them in
the File|Recent Projects submenu. You can quickly open a project by selecting from this
submenu.
When you exit JADE, if you have a project open, it will be remembered and
automatically reopened the next time you run JADE.
14.2.2 Project Tree
Every project has a scope that determines the files that comprise the project. JADE
automatically builds a project according to its scope.
The project tree displays a directory structure starting with the project folder. By
definition, the project scope is a subset of the project tree. Folders and files in this tree are
color-coded to show if they’re part of the scope:

An explicitly included node appears in bold blue font, and its implicitly-included
descendents appear in bold black font.

A neutral node appears in plain grey font. Neutral nodes are outside the project scope.

An explicitly excluded node appears in plain red font.
516
JavaGram Agile Development
When you create a new project, the whole project is initially in neutral state. You must
then include nodes that are part of the project scope, and exclude any sub-nodes that are
outside the project scope.
To change the scope of your project, select the nodes that you want in/out of the scope
and press one of these buttons (in the toolbar directly above the project tree):

Press the Include in Project (+) button (or choose the same from the Project menu, or
press Control+I) to include the nodes in the project scope.

Press the Inherit Parent’s Inclusion (=) button (or choose the same from the Project
menu, or press Control+T) to make the nodes conform to their parent. You only need
to do this to nodes that were previously forced to deviate from their parents’ settings
and now need to be restored.

Press the Exclude from Project (-) button (or choose the same from the Project menu,
or press Control+E) to exclude the nodes from the project scope.
To reset all your include/exclude commands, select Project|Reset All Include/Exclude.
Any of these changes causes JavaGram to do an incremental build to validate the scope.
For example, if you exclude a script that’s loaded by other included scripts then the latter
will show build errors due to the missing dependency.
Other operations you can perform on project tree nodes are as follows.
To open a script for editing, double-click the script’s node in the tree. A new tab is
added to the editing pane, displaying the script’s content ready for editing. If the script is
already open, its tab is simply brought to the front.
To create a new folder, select the parent folder and press the Create Folder button or
choose the same from the Project menu. The following dialog appears.
Type in the folder name and press the OK button to create the folder. The created folder
is added to the project tree and selected.
To delete tree nodes, select the nodes you want to delete and press the Delete
File/Folder button or choose the same from the Project menu. An alert box will appear,
asking you to confirm the delete.
JADE – JavaGram IDE
517
To refresh the project tree from disk storage, press the Refresh and Re-analyze button or
choose the same from the Project menu. Any changes made to the project directory
structure outside JADE are reflected in the tree. Scripts that need to be re-analyzed as a
result are re-analyzed. Alternatively, you can go a step further and choose
Project|Refresh and Reparse All. This effectively performs a clean build and is useful if
you’re experiencing spurious errors being reported.
To navigate to the currently-edited file, press the Show File in Tree button. The node
for the front-most file in the editor pane is selected in the project tree.
14.2.3 Outline Tree
The outline tab (next to the project tab) displays the class structure for the front-most
script in the editor pane. Use this tab to get a summary view of the structure of a script
and to navigate to its individual parts.
The outline is organized as a tree, where the root denotes the script, under which we have
a class node for each of the classes in the script, and under each class we have a list of the
class members.
The icon of each node identifies its type:

Classes appear as combs.

Methods appear as circles.

Fields appear as rectangles.

GUI members appear as hexagons bearing the letter ‘G’.

Text members appear as hexagons bearing the letter ‘T’.
The icons are color-coded to represent their visibility:
518
JavaGram Agile Development

Blue means domain visibility.

Green means public visibility.

Yellow means protected visibility.

Red means private visibility.
You can browse to the definition of a node by simply clicking on it – the first line of its
definition is selected in the editor.
14.2.4 Project Properties
To view the properties of the current project, press the Project Properties button or
choose the same from the Project menu. The following dialog is displayed.
The project name (e.g., Quest.jade) is displayed as read-only, because it represents the
project file.
A project keeps track of two root folders:

The Project Tree Root determines the root folder displayed in the project navigation
tree.

The JavaGram Root sets the root path of a running program (via the –root option).
Both these are usually set to the same folder, but they can be different. The Build Target
field specifies a target folder where the output of an explicit build is stored (as initiated
by Project|Build Project).
To change any of these, press the relevant button, navigate to the desired folder and select
it. Alternatively, you can enter the path directly into the field. Non-existing paths are
rejected.
JADE – JavaGram IDE
519
The Run Configurations tab allows you to define any number of run configurations for
your project. You can bring up this tab directly (without going through project properties)
by pressing the Runtime Configurations button in the main toolbar or choosing the same
from the Project menu.
Every new project contains a <Default> configuration to get you started.
To define a new run configuration, press the New button. If the new configuration is
similar to an existing one, it’s easier to select it, press the Copy button to make a copy of
it, and then make the necessary changes to it. To edit an existing configuration, select it
and press the Edit button. To delete it, press the Delete button. You can re-order the list
by selecting an entry and pressing the Move Up or Move Down button to move it
up/down the list.
Any change made to the list of run configurations is reflected in the combo box in the
main toolbar. To run a program, you select the desired configuration from this combo and
press the Run or Debug button next to it. For convenience, the last modified configuration
is automatically selected in this combo, ready for execution.
When you press New, Copy, or Edit, the following dialog appears.
520
JavaGram Agile Development
Enter the desired configuration name into the first field, and select its type from the
combo box. The latter allows you to specify the run mode by choosing one of these
options:

Standalone non-GUI App

Standalone GUI App

Client non-GUI App

Client GUI App

Client Browser App

Server
Choosing the correct mode is very important. For example, choosing ‘Client non-GUI
App’ for an application that has a GUI may cause it to misbehave.
The selected mode affects the rest of the dialog, so in explaining the remaining fields
we’ll indicate whether it’s relevant to a specific mode.
The Main Script field specifies the initial script for a program. This only applies to
standalone and server modes. Press the button, navigate to the desired script, and select it.
The Config File field specifies the program configuration file. This only applies to
standalone (optional) and server modes. Press the button, navigate to the desired
configuration file, and select it.
The Log File field optionally specifies a log file. When specified, all output sent to
standard output and standard error streams will go to this file instead. To use an existing
JADE – JavaGram IDE
521
file, press the button, navigate to the desired log file and select it. To specify a new file,
type its path into the field.
The Client Root file specifies the root path of a client program, so it only applies to the
client mode. Press the button, navigate to the desired folder, and select it. In a standard
installation, use C:/JavaGram/client/ as the root for native clients, and
C:/JavaGram/flash/ as the root for browser-based clients.
The Client Boot Script field specifies the initial script for a client, so it only applies to the
client mode. Because this script refers to a server-side file, it must be specified as a
relative path (relative to the server’s JavaGram root path).
The Client Boot Partition field applies to browser clients only and specifies the initial
partition for the application.
The Transport field applies to browser clients only and specifies the transport format for
client-server communication. Note that if you use http or https transport, you must
deploy your server in a servlet container (see Chapter 7).
The SSL Keystore field specifies the Java keystore file for a client, so it only applies to
the client mode. In a vanilla installation, you would set this to C:/JagClient/ssl/JAG.jks.
The Client/Server Host:Port field specifies the host and port number for a client or
server, so it only applies to client and server modes. The host can be specified using its
domain name or IP address. The host and port must be separated by a colon (e.g.,
localhost:443).
The Proxy Server Host:Port field applies to browser clients only and optionally specifies
a HTTP proxy server for the client to tunnel through.
The Application Stage optionally specifies the development stage of the application.
Possible values are: blank, dev, test, or prod.
The Timezone field optionally specifies the desired time zone for the application (e.g.,
"America/Los_Angeles"). Leaving it blank assumes your default installation timezone.
When ticked, the Verbose mode checkbox causes the program to run in verbose mode,
producing additional output (e.g., all client-server communication). The Suppress
warnings checkbox inhibits all warnings. The Clear cache checkbox causes the
program’s cache to be cleared before running (applies to client and server modes). The
Keep Std Out checkbox causes output redirected to a log file to also be sent to the
standard output stream.
The Override Java Pars field can be used to override the Java parameters specified in the
Tools|Preferences dialog (described later in this chapter). When not ticked, the field
522
JavaGram Agile Development
shows the current setting as read-only. To override it, tick the checkbox and enter the
new setting into the field. The new setting will only apply to this run configuration.
The Debugger Host:Port field specifies the internal host and port number for the JADE
debugger. You should leave this to its default setting (localhost:9900) unless the port is
conflicting with something else on your computer.
Once you’ve finished your changes, press OK to confirm them, or Cancel to dismiss
them. This takes you back to the project properties dialog.
14.2.5 Project Statistics
You can quickly generate statistics on your project by choosing Project|Statistics. Within
the dialog that appears, you have the option of limiting the statistics to the files in the
project scope, or scanning all files under the project root folder. Press the Scan button.
The result is displayed in the dialog shortly after, with a tally at the bottom of the dialog.
14.2.6 Building a Project
As stated earlier, JavaGram automatically and incrementally builds your project as you
work. The purpose of the incremental build is to report any errors immediately, so that
you can take action. No binary files are generated in this process – the build result is kept
in memory to aid performance.
The Project|Build Project command offers you a different style of build, where compiled
binary files are generated. Use this command for situations where you want to release an
application in binary (rather than source) format.
JADE – JavaGram IDE
523
The build commences as soon as you select this command. The Build Output tab appears
in the task pane and displays the build log. To cancel a lengthy build, press the Stop
Process button. To clear the output log, press the Clear Process Output button.
Before initiating a build, make sure that the target folder is correctly specified in the
project properties dialog. All compiled (.jax) files are written to this folder, following the
same folder hierarchy as the source files.
14.3 Editor
Each open file is displayed as a tab in the editor pane. JavaGram source ( .jag) files are
color-coded. All other text files are displayed without color coding.
The visual appearance of the editor is shown below. We’ve intentionally introduced an
error, a warning, and a breakpoint in the front-most tab to illustrate how they’re
highlighted.
The editor view is divided into these regions:

The left-most strip (line gutter) displays the line numbers for the file. If a line
contains an error or warning, its line number appears, respectively, in red or amber.

The grey strip (breakpoint gutter) to the right of the line gutter is reserved for
displaying breakpoints. Each breakpoint is displayed as a small ‘hand’ icon.

The editor’s text content is displayed in the middle, including scrollbars. This is
where you perform most of your editing operations using the mouse and keyboard.

The right-most grey strip (ruler) displays the relative position of errors/warnings in
the file as red/amber boxes. The small square at the top of the ruler goes green when
the file has no errors/warnings. Otherwise it goes red or amber.
524
JavaGram Agile Development
Identifiers and symbols in a file are color-coded according to a set of color-coding rules,
which can be customized by the user (see Tools|Preferences|Syntax Coloring). A code
fragment that is in error is displayed in bold red and underlined with a jagged line. A
code fragment that’s causing a warning is highlighted similarly but in amber.
The background of the cursor line (the line where the blinking cursor appears) is
displayed in light grey.
14.3.1 File Operations
To create a new script, press the New button in the main toolbar or choose File|New or
press Control+N. The Create New wizard dialog is displayed (see Section 14.2.1). Select
JavaGram Script from the wizard tree and press the Next button. The wizard changes to
the following.
JADE – JavaGram IDE
525
Enter the script name (the class name defaults to the same, but you can change it) and
press Finish. The script is created, added to the project tree, and opened in the editor (if
the Open checkbox is also ticked).
To open a file, locate it in the project tree and double-click it. Alternatively, press the
Open File button in the main toolbar or select the same from the File menu or press
Control+O, navigate to the desired file and select it. The file is loaded onto the editor.
To save changes to a file, press the Save button in the main toolbar or select the same
from the File menu or press Control+S. Alternatively, you can press the Save All button
or select the same from the File menu to save all modified open files (as well as any
changes to the project). To save a file under a different name, select File|Save As and
specify its new name in the displayed dialog.
To close a file, click in the cross icon of the file’s tab. Alternatively, make sure that it’s
the front-most tab and press the Close File button in the main toolbar or select the same
from the File menu or press Control+Q. If the file has any unsaved changes, you’ll be
asked as to whether you want to save the changes.
To close all open files, choose File|Close All Files. If any of the open files have any
unsaved changes, you’ll be asked as to whether you want to save the changes.
For convenience, JADE keeps a list of the last few opened files and displays them in the
File|Recent Files submenu. You can quickly open a file by selecting it from this
submenu.
To print a file, make sure that it’s the front-most tab in the editor and select File|Print or
press Control+P. The following print dialog (or something similar) is displayed.
526
JavaGram Agile Development
Make your selections and press the Print button. You can define your page setup either in
this dialog or using the File|Page Setup dialog.
14.3.2 Editing Operations
This section describes the editing operations that you can perform on an open file. It
assumes that the file is a JavaGram (.jag) script. You can also view a list of the
supported editing shortcuts by selecting Help|Shortcuts.
To view the details of an error/warning, place the cursor on it – the details will appear
in a tooltip. You can do this in the line gutter, in the editor content, or in the ruler.
To select a text segment, click at its beginning and hold the mouse button down, drag to
its end and release the mouse button. For a selection that spans across many lines, it may
be easier to click at the beginning, hold the shift key down, and then click at the end.
To select a token, double-click it.
To select an entire line, click its line number in the line gutter.
To copy text to the clipboard, select it and choose Edit|Copy or press Control+C.
To move text to the clipboard, select it and choose Edit|Cut or press Control+X.
To insert text, click where you want to insert and start typing or, if you have the text
already on the clipboard, paste it by choosing Edit|Paste or pressing Control+V.
JADE – JavaGram IDE
527
To replace text, select it and start typing or, if you have the text already on the clipboard,
paste it by choosing Edit|Paste or pressing Control+V.
To delete text, select it and press the backspace or delete key. Alternatively, click at the
beginning of the text to be deleted and repeatedly press the delete key, or click at its end
and repeatedly press the backspace key.
To toggle a line’s breakpoint, click in the breakpoint gutter next to it. Breakpoints can
be set only for lines that are capable of having breakpoints. To toggle the breakpoint of
the caret line, you can also choose Run|Toggle Breakpoint or press Control+B.
To clear all breakpoints for the project, choose Run|Clear Bkreapoints or press
Control+D.
To find out the type of an identifier, hold the Control key down and place the mouse
pointer over the identifier. If the identifier has a viewable type, it’s shown as a tooltip
(e.g., class summary, variable/field type, method signature).
To navigate to the definition of an identifier, hold the Control key down and place the
mouse pointer over the identifier. The identifier is highlighted (underlined) if it’s
navigate-able. Click and JADE will take you to its definition, which may be in the same
file or another file.
To indent one or more lines, select them (first and last lines may be partially selected)
and press the tab key. With each tab press, the lines are moved to the right by one tab
position. To left-indent the lines, press Shift+Tab instead.
To select the next word (or place the caret after the next close bracket), choose
Edit|Select Next or press Control+Tab.
To select the previous word (or place the caret after the previous open bracket), choose
Edit|Select Previous or press Control+Shift+Tab.
To comment or uncomment one or more lines, select them (or if it’s just one line, place
the cursor anywhere on the line) and choose Edit|Toggle Comments or press Control+/.
To move the caret forward one token at a time, press Control+►. To move back one
token, press Control+◄.
To view code insight for the current context, choose Tools|Code Insight or press
Control+Period.
To go to the start of the caret line, press the Home key.
To go to the start of the file, press Control+Home.
528
JavaGram Agile Development
To go to the end of the caret line, press the End key.
To go to the end of the file, press Control+End.
To select the entire file, choose Edit|Select All or press Control+A.
To go to a specific line of the file, choose Edit|Go to Line or press Control+G. Enter the
desired line number into the dialog that’s displayed and press OK.
To move the caret to a matching bracket, choose Edit|Go to Matching Bracket or press
Control+M.
To select from the caret to a matching bracket (assuming that the caret is just after a
bracket), choose Edit|Select to Matching Bracket or press Control+Shift+M.
To undo the last editing operation, choose Edit|Undo or press Control+Z. To redo an
undo, choose Edit|Redo or press Control+Y. Unlimited undo and redo operations are
supported. If you undo/redo up to a save point, the file’s ‘edited’ status (a * next to its
name in the editor tab) disappears.
The editor also supports a number of context sensitive behaviors (these can be switched
on/off via Tools|Preferences|Typing):

When you place the caret after a bracket (any type of bracket), this bracket and its
matching pair are highlighted.

When you place the caret on or after a markup tag, this tag and its matching pair are
highlighted.

When you type an opening bracket (any type of bracket), its closing bracket is
automatically inserted. If you type the closing bracket anyway, it will type over the
auto-inserted one. An auto-inserted closing brace is placed where you’re likely to
want it, having taken block indentation into account.

When you type a (single or double) quote character, its closing quote is automatically
inserted. If you type the closing quote anyway, it will type over the auto-inserted one.

When you press enter to open a new line, the caret on the new line is indented to
where you’re likely to want to start typing.
14.3.3 Code Insight
Code insight is a popup that’s displayed automatically as you type your code, offering
you code completion suggestions. It’s activated by the following triggers (which can be
switched on/off via the Tools|Preferences|Typing):

Typing the opening < character of a markup tag (e.g., <Panel>). Code insight will
display all possible tags that can be inserted at the caret point.
JADE – JavaGram IDE
529

Typing the first character of an element’s property (e.g., border property of a
<Panel>). Code insight will display all permissible properties for the element.

Typing the = character after an element’s property name whose values form an
enumeration (e.g., lay property of a component). Code insight will display the
enumeration values.

Typing the period (.) character after a class name (including pseudo classes). Code
insight will display all public static members of the class.

Typing the period (.) character after an object’s identifier. Code insight will display
all members of the object’s class that are visible in the current context.
You can also explicitly activate code insight by choosing Tools:Code Insight or pressing
Control+Period. This method is useful when you don’t want to cause any change to the
file.
Here is an example of a code insight popup.
When the popup is visible, you can do any of the following:

Narrow your choices by typing the first few characters of the member you’re after.
The list is filtered and only members that match the typed prefix are displayed.

Press the Up/Down arrow key to move up/down the list one row at a time.

Press the Enter key to select the current row, or double-click the desired member. The
code for that member is inserted into the file at the caret position, replacing any prefix
that you might have typed in the process.

Press the Escape key or click outside the popup to dismiss it.
14.3.4 Reformatting a File
JADE has a built-in code formatter that enforces a coding style similar to Kernighan and
Ritchie. You can instantly reformat a file by choosing Tools|Reformat File or pressing
Control+R, provided your file has no syntax errors. This operation is fully undoable.
530
JavaGram Agile Development
14.3.5 Viewing a Script in HTML
If you want to copy code from the editor and paste it to another application, you have
three choices:

Use the Copy button in the toolbar or choose the same from the Edit menu. The
selected code will be copied as plain text (none of the syntax coloring or bold/italic
styling of the code will be preserved).

Choose the View|View as Color HTML command, which will display the front-most
code tab in a separate dialog, rendered in HTML that’s formatted to exactly match the
coloring and styling you see in the editor tab. You can then copy from this dialog and
paste it into your target application.

Choose the View|View as Plain HTML command. This has the same effect as the
second option, except that the HTML will be in black and white, but will including
bold/italic styling. The code snippets in this book were included using this approach.
Here is an example of what the second option produces.
You can also right-click on this dialog and choose the Save As command to save the
HTML view in a file. This is useful when you want to publish a script on a web page.
14.4 Searching and Refactoring
14.4.1 Searching a File
To search for a string in a file, press the Find button in the main toolbar or choose the
same from the Search menu. The following dialog is displayed.
JADE – JavaGram IDE
531
Type in the search string into the Find field and press the Find button (if you have a
string selected in the editor before you bring up this dialog, it will automatically appear in
the Find field). The editor will scroll to the next instance of the search string (starting
from the caret position) and highlight it. The Find/Replace dialog remains visible after a
search so that you can do more searches.
When ticked, the Case Sensitive checkbox forces case-sensitive matching of the search
string. Similarly, the Whole Word checkbox requires the search string to match a whole
word.
To find the next instance of the search string, press Find again. The search wraps
around when it reaches the end of the file.
To replace a string with another string, type the latter into the Replace field. Press Find
until you get to the instance you want to replace, and press Replace. Alternatively, press
Replace and Find to do a replace followed by a find.
To replace all instances of a string in a file, enter the search and replace strings and
press Replace All.
When the dialog is dismissed, the search and replace strings are remembered, so that you
can apply them without the dialog:

To find the next match, choose Search|Find Next or press the F3 key.

To replace the current match and find the next one, choose Search|Replace and Find
Next or press the F4 key.
14.4.2 Searching a Project
You can do find and replace across multiple files or even an entire project. First,
nominate your search scope by selecting a folder in the project tree, or select the project
node for a full project search. Then press the Find/Replace in Folder button in the main
toolbar or choose the same from the Search menu. A quicker way of doing both steps is
to right-click on a node in the project tree and select Find from the popup menu. The
following dialog is displayed (assuming that the search scope is the quest folder in the
sample application).
532
JavaGram Agile Development
When ticked, the Limit search checkbox ignores files that are outside the project scope.
The other fields of this dialog are similar to the Find/Replace dialog for single files.
However, rather than highlighting the search/replace result in the editor, it’s shown in the
Search/Refactor Results tab of JADE’s task pane.
To navigate to each search/replace result, simply click on its node in the result tree.
Because Replace All is not undoable, the user is prompted with the number of
replacements to be performed and asked to confirm the operation.
14.4.3 Refactoring
When you right-click on a project tree node, a popup menu is displayed that offers you
the following refactoring options:

Rename allows you to rename a folder or file name.

Move allows you to move a folder or file name to a different location – the affected
domain names are updated accordingly.

Duplicate allows you to duplicate a file to a nominated target folder, changing its
domain name accordingly.

Find displays the Find/Replace in Folder dialog explained earlier.

Project References applies only to files and displays instances where the file is loaded
by other files.
14.5 Running
Before running an application, make sure that you have a suitable run configuration
defined for it (see Section 14.2.4).
To run an application, select its desired configuration from Run Configurations combo
in the main toolbar and press the Run button next to it or choose the same from the Run
JADE – JavaGram IDE
533
menu or press F9. A new tab is added to JADE’s task pane, bearing the run
configuration’s name. The application is run as a separate process, whose output is
redirected to this tab.
The first line that appears in the process output tab is the actual command line JADE has
used to run it. JADE builds this command line based on information defined in
Tools|Preferences, project properties, and the run configuration.
To stop the application, press the Stop Process button.
To re-run the process, press the Restart Process button.
To clear the process output, press the Clear Process Output button. When a process is
restarted, its output is automatically cleared from the tab.
You can run multiple processes at the same time by repeating the above sequence for
running a process. Additional tabs will appear to represent them. For example, to test a
client-server application, you can run a server and a client in this way.
As a convenience, if you stop a running process and run the same configuration again
from the toolbar or the Run menu, the existing run tab will be reused if it’s the front-most
tab.
To remove a run tab, press the cross icon next to its tab name. If the application is still
running, it will be terminated.
14.6 Debugging
The steps for running an application in debug mode are the same as a normal run, except
that instead of pressing the Run button, you press the Debug button or choose the same
from the Run menu or press Shift+F9.
The appearance of a debug run tab is shown below. For this example, we’ve setup a
breakpoint, and the picture shows the debugger stopping at the breakpoint line.
534
JavaGram Agile Development
The debug tab has three sub-tabs. The console sub-tab displays the process output (as in a
normal run).
The debug information sub-tab displays the runtime stacks for the process threads, as
well as a Variables and an Expressions tab.
Each node in the stack tree represents a method call. If you click on a node, the editor
will navigate to the location of that call. At the same time, the Variables and Expressions
tabs are updated to show data relevant to the selected stack frame.
JADE – JavaGram IDE
535
The Variables ‘watch’ tab displays a table of variables that are visible at the currentlyselected stack frame. If you click on a variable in this table, its value will be displayed in
the adjacent panel. When the As Hierarchy checkbox is ticked, this value is displayed as
an expandable tree. Otherwise, it’s displayed as a literal in clear text.
The Expressions ‘watch’ tab is similar, except that instead of variables, you maintain a
list of expressions here. These expressions are evaluated in the context of the currentlyselected stack frame. If an expression can’t be evaluated, instead of a value, an error is
displayed.
To add a new expression to the list, press the Create Watch Item button, type the
expression in the Expr field, and press the Enter key. There are also buttons for applying
changes to an expression, deleting it, clearing it, and reordering the list. A useful shortcut
for adding an expression to the list is to select the expression in the editor and choose
Run|Add Selection to Watch or press Control+W.
The toolbar at the extreme left of the debug tab allows you to direct the flow of control.
The first three buttons behave as for a normal run. The remaining buttons are:

The Pause Process button (disabled in the above picture) is enabled when the process
is running and hasn’t stopped at a breakpoint. This button is useful when your process
has become unresponsive. For example, if the process is caught in an infinite loop,
pressing this button will cause it to pause inside the loop, thus pinpointing the loop
that’s misbehaving.

The Continue button is enabled when the debugger has stopped at a breakpoint.
Pressing it will cause the debugger to continue from that point.

The Step Into button causes the debugger to ‘step into’ the breakpoint line. If the line
is a method call then the debugger will progress execution to the beginning of the
method body and wait there.

The Step Over button causes the debugger to execute the breakpoint line and wait on
the next line.

The Step Out Of button causes the debugger to complete the execution of the current
method, return to the calling line, and wait there.

The Run to Caret Line causes the debugger to continue execution until it reaches the
caret line.
536
JavaGram Agile Development
The loaded files sub-tab shows the list of scripts so far loaded by the running process
(and their line count). This is for information only. You can navigate to each script by
simply clicking on its node.
Finally, to get a central view of all the breakpoints defined for your project, choose
View|Show Breakpoints tab. A tab similar to the following appears in the task pane of
JADE. It lists every breakpoint. To navigate to a breakpoint, simply click on its node.
14.7 Tools
14.7.1 Import Java Classes
JADE allows you to import Java classes and wrap them as JavaGram classes. This serves
as a quick and convenient way of accessing third-party Java functionality. The Java
classes may be provided as either a directory of class files or a JAR file. JADE processes
the Java classes and, for each class, displays its public members, allowing you to choose
the ones that you want to import. A JavaGram script is generated for each imported class.
To import one or more Java classes, first select the target folder in your project
navigation tree, and then select Tools|Import Java Classes. The following dialog is
displayed.
JADE – JavaGram IDE
537
If your Java classes reside in a directory, press the Class Dir button, navigate to the
directory and select it. If your Java classes reside in a JAR file, press JAR File, navigate
to the JAR file and select it.
Once selected, the import source appears in the text field to the left of the Scan button.
Press Scan to process the Java classes. After a short delay, the list box to the left of the
dialog is populated with the available classes, and the Filter combo above it is populated
with the package names. The latter is useful when the import source contains a large
number of classes. By choosing from this combo, you can reduce your view to classes in
a specific package.
When you select a class from this list, its members appear in the list box to the right of
the dialog. Tick the classes you want to import and, for each of these, tick the class
members you want to import. A set of buttons below the list boxes allow you to quickly
tick or untick all listed items.
Note that the Target File(s) field is pre-populated with the folder you had selected in the
project navigation tree before displaying the import dialog. The combo to the right of this
field gives you two options: (i) ‘blank’ (default) causes the generated files to be directly
placed in the nominated folder, or (ii) ‘Use Folder Names’ causes sub-directories to be
generated within the nominated folder that match the imported class package hierarchy.
Press Import to commence importing the files. The imported files are added to the
nominated folder in the project navigation tree, ready for viewing.
538
JavaGram Agile Development
14.7.2 Preferences
To view or edit JADE’s user preferences, select Tools|Preferences. Once you’ve made
your changes, press the OK button for them to take effect. Confirmed changes are
remembered indefinitely.
The preference categories are displayed as a tree. The Workspace category defines a
number of options that affect the JADE workspace.
The Search category defines the default options for search dialogs.
The Editor category defines certain options that affect the appearance of the editor.
JADE – JavaGram IDE
539
The Syntax Coloring category allows you to customize the color-coding rules of the
editor.
The Typing category controls the behavior of the editor in response to the user typing
certain meta characters.
540
JavaGram Agile Development
The Java category allows you to define the Java settings for the purpose of running an
application.
The JavaGram category allows you to define the default settings for a run configuration.
Every time you create a new run configuration, it inherits its initial settings from the
preferences defined here, which you can then override in the run configuration, if desired.
JADE – JavaGram IDE
541
The Flash category allows you to nominate the preferred browser(s) for running browser
clients.
542
JavaGram Agile Development
15
JavaGram Syntax
This chapter describes the formal syntax of JavaGram. The syntax has been intentionally
designed to be as close as possible to Java to make it easier for Java programmers to
assimilate. A major addition is the use of a markup notation for defining user interface
and text class members. This style was chosen due to its expressiveness for defining
hierarchical information.
15.1 Conventions
The JavaGram syntax is specified as a set of BNF-style production rules. The following
conventions apply.

The ::= operator means that the left-hand-side can be substituted for by the righthand-side.

Terminals are enclosed in single quotes. These are typically keywords, operators, or
meta symbols.

The | operator means ‘or’. It specifies a choice of alternatives.

{...}* means that the contents may appear zero or more times.

{…}+ means that the contents may appear one or more times.

{…}? means that the contents are optional (i.e., may appear zero or once).

Parentheses are used to eliminate ambiguities related to the use of | in complex
productions.

Explanatory comments appear in italics.
The production rules are divided into two groups. The first group (JAG) covers
JavaGram’s syntax, as visible to the programmer. The second group (JTP) covers the
production rules for client-server messaging – these are hidden from the programmer and
managed by the JAG runtime.
The only constructs not covered by the production rules are comments. Comments are
eliminated during lexical analysis. There are three types of comments:

Single line comments (e.g., // some comment).

Comments that can span across multiple lines (e.g., /* some comment */).

Internal line control comments (e.g., /#122#/ which causes the parser to treat the
current line as being line 122). These are not for programmer use.
15.2 JAG Production Rules
Target ::=
JavaGram Syntax
543
JavaGram
JavaGram ::=
Literal
| '<jag' TagProperties '>' {LoadMarkup | ClassDeclaration | Literal}* '</jag>'
A script may contain just a literal (useful for configuration and data files). Also, literals may
appear within <jag> markup (enables the IDE to parse and reformat such files correctly).
Valid script tag attributes are:
domain = "abc/xyz"
mjv = "Min JavaGram Version in the format majorVer.minorVer"
stage = ('$dev' | '$test' | '$prod')
side = ('$client' | '$server' | '$both')
TagProperties ::=
{Identifier '=' TagValue}*
TagValue ::=
Literal
| Type
| '{' Expression '}'
LoadMarkup ::=
'<load' TagProperties '>' {RelativeFilePath}* '</load>'
Valid load tag properties are:
relative = ('true' | 'false')
source = ('$local' | '$boot' | SocketStream)
RelativeFilePath ::=
'"' {DirectoryName '/'}* FileName '"'
ClassDeclaration ::=
{ClassQualifier}* 'class' Identifier {'extends' QualifiedId {',' QualifiedId}* }?
'{' {ClassBodyDeclaration}* '}'
QualifiedId ::=
Identifier {'\' Identifier}*
ClassBodyDeclaration ::=
ClassBodyDeclarationNoConstructor
| ConstructorDeclaration
ClassBodyDeclarationNoConstructor ::=
StaticInitializer
| FieldDeclaration
| MethodDeclaration
| GuiMarkupDeclaration
544
JavaGram Agile Development
| TextMarkupDeclaration
StaticInitializer ::=
'static' Block
FieldDeclaration ::=
{FieldQualifier}* VariablesDeclarator ';'
MethodDeclaration ::=
{MethodQualifier}* (Type | 'void') Identifier MethodParameters (';' | Block)
ConstructorDeclaration ::=
{MethodQualifier}* Identifier MethodParameters ConstructorBody
GuiMarkupDeclaration ::=
{FieldQualifier}* '<'GuiTag {Identifier}? TagProperties
('/>' | '>' {GuiMarkupDeclaration}* '</'GuiTag'>')
GuiTag ::=
Identifier TagQualifiers
TextMarkupDeclaration ::=
{MethodQualifier}* '<'TextTag TextMethod TagProperties '>' Text '</'TextTag'>'
TextTag ::=
'text' TagQualifiers
TagQualifiers ::=
{'.' Identifier {'.' Identifier}? }?
TextMethod ::=
Type Identifier MethodParameters
ClassQualifier ::=
'abstract'
| 'final'
| 'clocal'
| 'slocal'
| 'remote'
| 'singleton'
| 'mutual'
| 'generated'
FieldQualifier ::=
'public'
| 'protected'
JavaGram Syntax
545
| 'private'
| 'static'
| 'final'
| ‘delayed’
| 'generated'
| 'getable'
| 'setable'
MethodQualifier ::=
'public'
| 'protected'
| 'private'
| 'static'
| 'abstract'
| 'final'
| 'synchronized'
| 'clocal'
| 'slocal'
| 'remote'
| 'generated'
VariablesDeclarator ::=
Type VariableDeclarator {, VariableDeclarator}*
Type ::=
'string'
| 'symbol'
| 'int'
| 'real'
| 'char'
| 'boolean'
| 'date'
| 'vague'
| 'stream'
| 'native'
| 'object'
| 'list'
| 'vector' {'<' Type '>'}?
| 'map' {'<' Type ',' Type '>}?
| QualifiedId
VariableDeclarator ::=
Identifier {AssignOpr Expression}?
AssignOpr ::=
'='
546
JavaGram Agile Development
| '@='
Expression ::=
ConditionalExpr
| Assignment
ConditionalExpr ::=
LogicalOrExpr {'?' Expression ':' ConditionalExpr}?
LogicalOrExpr ::=
LogicalAndExpr {'||' LogicalOrExpr}?
LogicalAndExpr ::=
BitwiseOrExpr {'&&' LogicalAndExpr}?
BitwiseOrExpr ::=
BitwiseXorExpr {'|' BitwiseOrExpr}?
BitwiseXorExpr ::=
BitwiseAndExpr {'^' BitwiseXorExpr}
BitwiseAndExpr ::=
EqualityExpr {'&' BitwiseAndExpr}?
EqualityExpr ::=
RelationalExpr {('==' | '?=' | '!=' | '===' | '!==') EqualityExpr}?
RelationalExpr ::=
ShiftExpr {('<' | '>' | '<=' | '>=') RelationalExpr}?
| ShiftExpr 'instanceof' Type
ShiftExpr ::=
AdditiveExpr {('<<' | '>>') ShiftExpr}?
AdditiveExpr ::=
MultiplicativeExpr {('+' | '-') AdditiveExpr}?
MultiplicativeExpr ::=
UnaryExpr {('*' | '/' | '%') MultiplicativeExpr}?
UnaryExpr ::=
('++' | '--' | '+' | '-' | '~' | '!') UnaryExpr
| PostfixExpr
PostfixExpr ::=
Primary
JavaGram Syntax
547
| PostfixExpr ('++' | '--')
Primary ::=
Literal
| 'this'
| 'super' {'@' QualifiedId}?
| QualifiedId {'.' ('symbol' | 'singleton')}?
| '(' Expression ')'
| 'arg' '(' Expression ')'
| Primary '@' (Type | '<' GuiTag '>')
| 'typeof' '(' Expression ')'
| 'valueof' '(' Expression {',' Expression}? ')'
| ('sys' | 'gui' | 'sql' | 'bom') '.' Identifier {'@' Type}? {ExprList}?
| NewExpr
| ObjectCreation
| ListCreation
| VectorCreation
| MapCreation
| FieldAccess
| PropertyAccess
| MethodCall
| VecMapOrFieldAccess
| DelayedString
Literal ::=
StringLiteral
| SymbolLiteral
| IntLiteral
| RealLiteral
| CharLiteral
| DateLiteral
| BinaryLiteral
| ObjectLiteral
| ProxyObjectLiteral
| ListLiteral
| VectorLiteral
| MapLiteral
| XmlLiteral
| 'false'
| 'true'
| 'null'
DelayedString ::=
$ StringLiteral
DelayedString and Text may contain expressions of the form {...} which are evaluated when
the parent is evaluated.
548
JavaGram Agile Development
NewExpr ::=
'new' QualifiedId ExprList { '{' {ClassBodyDeclarationNoConstructor}* '}' }?
ExprList ::=
'(' {Expression {',' Expression}* }? ')'
ObjectCreation ::=
'object' '(' QualifiedId {',' Identifier '=>' Expression }* ')'
ListCreation ::=
'list' ExprList
VectorCreation ::=
'vector' ExprList
MapCreation ::=
'map' '(' {Expression '=>' Expression {',' Expression '=>' Expression }* }? ')'
FieldAccess ::=
Primary ('.' | '?.') (Identifier | 'class')
PropertyAccess ::=
Identifier '.' PropertyName
MethodCall ::=
{TargetStream '::'}? {Primary ('.' | '?.')}? Identifier ExprList
TargetStream ::=
Primary
VecMapOrFieldAccess ::=
Primary ('[' Expression ']' | '.' SymbolLiteral)
Identifier ::=
(Letter | '_') {Letter | Digit | '_'}*
PropertyName ::=
Identifier
StringLiteral ::=
'"' {Char}* '"'
SymbolLiteral ::=
'$' (Identifier | '{' {Char)+ '}')
JavaGram Syntax
549
IntLiteral ::=
{Digit}+ | '0' {OctDigit}+ | '0' ('x' | 'X') {HexDigit}*
RealLiteral ::=
{Digit}* '.' {Digit}* {('e' | 'E') {'+' | '-'}? {Digit}+ }?
| {Digit}+ ('e' | 'E') {'+' | '-'}? {Digit}+
OctDigit ::=
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7'
Digit ::=
OctDigit | '8' | '9'
HexDigit ::=
Digit | 'A' | 'a' | 'B' | 'b' | 'C' | 'c' | 'D' | 'd' | 'E' | 'e' | 'F' | 'f'
CharLiteral ::=
"'" ( Char | '\' Char | '\u' {HexDigit}4 ) "'"
DateLiteral ::=
'[#' YearMonthDay {HoursMinutesSecondsNanos}? | MillisecondsNanos} ']'
BinaryLiteral ::=
'[##' {HexDigit}* ']'
ObjectLiteral ::=
'[@' QualifiedId (LiteralFields | LiteralNamedFields) ']'
LiteralFields ::=
{Literal {',' Literal}* }?
LiteralNamedFields ::=
{Identifer '=>' Literal {',' Identifier '=>' Literal}* }?
ProxyObjectLiteral ::=
'[@' QualifiedId '#' IntLiteral ']'
YearMonthDay ::=
{Digit}+ ('-' | '/') {Digit}+ ('-' | '/') {Digit}+
HoursMinutesSecondsNanos ::=
{Digit}+ ':' {Digit}+ ':' {Digit}+ {'.' {Digit}+ }?
MillisecondsNanos ::=
{Digit}+ {'.' {Digit}+ }?
550
JavaGram Agile Development
Text ::=
{Char}+
ListLiteral ::=
'$(' Literal {',' Literal}* ')'
Inside a literal, a list can begin with ( instead of $(
VectorLiteral ::=
'[' {Literal {',' Literal}* }? ']'
MapLiteral ::=
'[' Literal '=>' Literal {',' Literal '=>' Literal }* ']'
| '[=>]'
XmlLiteral ::=
'<#' XmlData '#>'
Assignment ::=
LeftHandSide
(AssignOpr | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '&=' | '^=' | '|=') Expression
LeftHandSide ::=
Indentifier
| FieldAccess
| VecMapOrFieldAccess
| PropertyAccess
| ‘sys’ ‘.’ Identifier
MethodParameters ::=
'(' ( '...' | {MethodParameter {',' MethodParameter}* }? ) ')'
MethodParameter ::=
{'final'}? Type VariableDeclarator
ConstructorBody ::=
'{' ( {'this' ExprList ';'}? | {'super' {'@' QualifiedId}? ExprList ';'}* )
{BlockStmt}* '}'
Block ::=
'{' {BlockStmt}* '}'
BlockStmt ::=
{'final'}? VariablesDeclarator ';'
| Statement
Statement ::=
JavaGram Syntax
551
Block
| ';'
| 'if' '(' Expression ')' Statement {'else' Statement}?
| 'while' '(' Expression ')' Statement
| 'for' '(' {ForInit}? ';' {Expression}? ';' {ForUpdate}? ')' Statement
| 'for' '(' Type Identifier 'in' Expression ')' Statement
| 'switch' '(' Expression ')' SwitchBlock
| 'do' Statement 'while' '(' Expression ')' ';'
| 'break' ';'
| 'continue' ';'
| 'return' {Expression}? ';'
| 'synchronized' '(' Expression ')' Block
| 'throw' Expression ';'
| 'try' Block {CatchClause}* {'finally' Block}?
| 'query' Connections Block
| 'transaction' Connections Block
| 'assert' Expression ';'
| StatementExpr ';'
| AsyncMethodCall ';'
SwitchBlock ::=
'{' {{SwitchLabel}+ {BlockStatement}* }* '}'
SwitchLabel ::=
('case' CaseExpr | 'default') ':'
CaseExpr ::=
IntLiteral
| CharLiteral
| {QualifiedId '.'} Identifier
CatchClause ::=
'catch' '(' QualifiedId Identifier ')' Block
ForInit ::=
{StatementExpr {',' StatementExpr}* }?
| VariablesDeclarator
ForUpdate ::=
{StatementExpr {',' StatementExpr}* }?
StatementExpr ::=
Expression
Connections ::=
'(' Expression {',' Expression}* ')'
552
JavaGram Agile Development
AsyncMethodCall ::=
{LeftHandSide AssignOpr | Type Identifier AssignOpr}?
MethodCall { {('??' | '?>') Expression}? '->' AsyncCallback { '->' AsyncCallback }?
}?
AsyncCallback::=
Primary | Block
15.3 JTP Production Rules
All client requests and server responses contain a session identifier (sid) and request
identifier (rid). These are used to keep track of the identity of requests/responses and to
match them where required.
ClientRequest ::=
'<req' TagProperties ('/>' | '>' Expression '</'req'>')
{BinaryData}?
Valid tag properties are:
sid = IntLiteral
rid = IntLiteral
timestamp = IntLiteral
size = IntLiteral
zip = IntLiteral
line = IntLiteral
id = IntLiteral
idx = IntLiteral
kind = "sys" | "app"
err = StringLietral
cmd = StringLiteral
context = StringLietral
script = StringLietral
path = StringLietral
file = StringLietral
var = StringLiteral
method = StringLiteral
key = StringLiteral
file_attach = StringLiteral
async = BooleanLiteral
relative = BooleanLiteral
code = BooleanLiteral
check = BooleanLiteral
base = BooleanLiteral
static = BooleanLiteral
value = Literal
JavaGram Syntax
553
ServerResponse ::=
'<res' TagProperties ('/>' | '>' {Literal}? {';' ObjectLiteral}? '</'res'>')
{BinaryData}?
ObjectLiteral after semicolon is the implicit object for non-static remote calls
Valid tag properties are:
sid = IntLiteral
rid = IntLiteral
line = IntLiteral
start = IntLiteral
length = IntLiteral
timestamp = IntLiteral
env = IntLiteral
frame = IntLiteral
kind = "ok" | "err"
broadcast = StringLiteral
path = StringLietral
file = StringLietral
ext = StringLiteral
file_attach = StringLiteral
async = BooleanLiteral
value = Literal
BinaryData ::=
{Byte}+
Used for communicating compressed message data or binary file content.
InternalNativeLiteral ::=
'[@@' QualifiedId Properties ']'
Properties ::=
{Identifier '=>' Expression {',' Identifier '=>' Expression}* }?
InternalClassNameAtLocation ::=
QualifiedId '@@' RelativeFilePath
Used as a valid Factor in client-server communication to represent a reference to a class
name as a value. It tells the server where the class is.
554
JavaGram Agile Development