Search The Web

Thursday, April 30, 2009

GUI programming - the Asynchronous Programming Model

Unlike conventional computer programs, that carry some serial nature, a GUI program usually uses an asynchronous programming model, also known as "event-driven programming". This means that that program mostly sits idle, waiting for events sent by the X server, and then acts upon these events. An event may say "The user pressed the 1st button mouse in spot x,y", or "the window you control needs to be redrawn". In order for the program to be responsive to the user input, as well as to refresh requests, it needs to handle each event in a rather short period of time (e.g. less than 200 milliseconds, as a rule of thumb).

This also implies that the program may not perform operations that might take a long time while handling an event (such as opening a network connection to some remote server, or connecting to a database server, or even performing a long file copy operation). Instead, it needs to perform all these operations in an asynchronous manner. This may be done by using various asynchronous models to perform the longish operations, or by performing them in a different process or thread.

So the way a GUI program looks is something like that:

  1. Perform initialization routines.
  2. Connect to the X server.
  3. Perform X-related initialization.
  4. While not finished:
    1. Receive the next event from the X server.
    2. handle the event, possibly sending various drawing requests to the X server.
    3. If the event was a quit message, exit the loop.
  5. Close down the connection to the X server.
  6. Perform cleanup operations.

Basic Xlib Notions

In order to eliminate the needs of programs to actually implement the X protocol layer, a library called 'Xlib' was created. This library gives a program a very low-level access to any X server. Since the protocol is standardized, A client using any implementation of Xlib may talk with any X server. This might look trivial these days, but back at the days of using character mode terminals and proprietary methods of drawing graphics on screens, this looked like a major break-through. In fact, you'll notice the big hype going around thin-clients, windows terminal servers, etc. They are implementing today what the X protocol enabled in the late 80's. On the other hand, the X universe is playing a catch-up game regarding CUA (common user access, a notion made by IBM to refer to the usage of a common look and feel for all programs in order to ease the lives of the users). Not having a common look and feel was a philosophy of the creators of the X window system. Obviously, it had some drawbacks that are evident today.


The X Display

The major notion of using Xlib is the X display. This is a structure representing the connection we have open with a given X server. It hides a queue of messages coming from the server, and a queue of pending requests that our client intends to send to the server. In Xlib, this structure is named 'Display'. When we open a connection to an X server, the library returns a pointer to such a structure. Later, we supply this pointer to any Xlib function that should send messages to the X server or receive messages from this server.


The GC - Graphics Context

When we perform various drawing operations (graphics, text, etc), we may specify various options for controlling how the data will be drawn - what foreground and background colors to use, how line edges will be connected, what font to use when drawing some text, etc). In order to avoid the need to supply zillions of parameters to each drawing function, a graphical context structure, of type 'GC' is used. We set the various drawing options in this structure, and then pass a pointer to this structure to any drawing routines. This is rather handy, as we often needs to perform several drawing requests with the same options. Thus, we would initialize a graphical context, set the desired options, and pass this GC structure to all drawing functions.


Object Handles

When various objects are created for us by the X server - such as windows, drawing areas and cursors - the relevant function returns a handle. This is some identifier for the object that actually resides in the X server's memory - not in our application's memory. We can later manipulate this object by supplying this handle to various Xlib functions. The server keeps a mapping between these handles and the actual objects it manages. Xlib provides various type definitions for these objects (Window, Cursor, Colormap and so on), which are all eventually mapped to simple integers. We should still use these type names when defining variables that hold handles - for portability reasons.


Memory Allocation For Xlib Structures

Various structure types are used in Xlib's interface. Some of them are allocated directly by the user. Others are allocated using specific Xlib functions. This allows the library to initialize properly these structures. This is very handy, since these structures tend to contain a lot of variables, making it rather tedious for the poor programmer to initialize. Remember - Xlib tries to be as flexible as possible, and this means it is also as complex as it can get. Having default values will enable a beginner X programmer to use the library, without interfering with the ability of a more experienced programmer to tweak with these zillions of options.

As for freeing memory, this is done in one of two ways. In cases where we allocated the memory - we free it in the same manner (i.e. use free() to free memory allocated using malloc()). In case we used some Xlib function to allocate it, or we used some Xlib query method that returns dynamically allocated memory - we will use the XFree() function to free this memory block.

Events

A structure of type 'XEvent' is used to pass events received from the X server. Xlib supports a large amount of event types. The XEvent structure contains the type of event received, as well as the data associated with the event (e.g. position on the screen where the event was generated, mouse button associated with the event, region of screen associated with a 'redraw' event, etc). The way to read the event's data depends on the event type. Thus, an XEvent structure contains a C language union of all possible event types (if you're not sure what C unions are, it is time to check your proffered C language manual...). Thus, we could have an XExpose event, an XButton event, an XMotion event, etc.


Compiling Xlib-Based Programs

Compiling Xlib-Based programs requires linking them with the Xlib library. This is done using a compilation command like this:


cc prog.c -o prog -lX11
If the compiler complains that it cannot find the X11 library, try adding a '-L' flag, like this:

cc prog.c -o prog -L/usr/X11/lib -lX11
or perhaps this (for a system with release 6 of X11):

cc prog.c -o prog -L/usr/X11R6/lib -lX11
On SunOs 4 systems, the X libraries are placed in /usr/openwin/lib:

cc prog.c -o prog -L/usr/openwin/lib -lX11
and so on...

Basic OpenGL Lighting.

Introduction.

Many people starting out with OpenGL are confused by the way that OpenGL's built-in lighting works - and consequently how colour functions. I hope to be able to clear up some of the confusion. What is needed to explain this clearly is a flow chart:

Lighting ENABLED or DISABLED?

The first - and most basic - decision is whether to enable lighting or not.
   glEnable ( GL_LIGHTING ) ;

...or...
   glDisable ( GL_LIGHTING ) ;

If it's disabled then all polygons, lines and points will be coloured according to the setting of the various forms of the glColor command. Those colours will be carried forward without any change other than is imparted by texture or fog if those are also enabled. Hence:
   glColor3f ( 1.0f, 0.0f, 0.0f ) ;

...gets you a pure red triangle no matter how it is positioned relative to the light source(s).

With GL_LIGHTING enabled, we need to specify more about the surface than just it's colour - we also need to know how shiney it is, whether it glows in the dark and whether it scatters light uniformly or in a more directional manner.

The idea is that OpenGL switches over to using the current settings of the current 'material' instead of the simplistic idea of a polygon 'colour' that is sufficient when lighting is disabled. We shall soon see that this is an over-simplistic explanation - but keep it firmly in mind.

glMaterial and glLight

The OpenGL light model presumes that the light that reaches your eye from the polygon surface arrives by four different mechanisms:
  • AMBIENT - light that comes from all directions equally and is scattered in all directions equally by the polygons in your scene. This isn't quite true of the real world - but it's a good first approximation for light that comes pretty much uniformly from the sky and arrives onto a surface by bouncing off so many other surfaces that it might as well be uniform.
  • DIFFUSE - light that comes from a particular point source (like the Sun) and hits surfaces with an intensity that depends on whether they face towards the light or away from it. However, once the light radiates from the surface, it does so equally in all directions. It is diffuse lighting that best defines the shape of 3D objects.
  • SPECULAR - as with diffuse lighting, the light comes from a point souce, but with specular lighting, it is reflected more in the manner of a mirror where most of the light bounces off in a particular direction defined by the surface shape. Specular lighting is what produces the shiney highlights and helps us to distinguish between flat, dull surfaces such as plaster and shiney surfaces like polished plastics and metals.
  • EMISSION - in this case, the light is actually emitted by the polygon - equally in all directions.
So, there are three light colours for each light - Ambient, Diffuse and Specular (set with glLight) and four for each surface (set with glMaterial). All OpenGL implementations support at least eight light sources - and the glMaterial can be changed at will for each polygon (although there are typically large time penalties for doing that - so we'd like to minimise the number of changes).

The final polygon colour is the sum of all four light components, each of which is formed by multiplying the glMaterial colour by the glLight colour (modified by the directionality in the case of Diffuse and Specular). Since there is no Emission colour for the glLight, that is added to the final colour without modification.

A good set of settings for a light source would be to set the Diffuse and Specular components to the colour of the light source, and the Ambient to the same colour - but at MUCH reduced intensity, 10% to 40% seems reasonable in most cases.

For the glMaterial, it's usual to set the Ambient and Diffuse colours to the natural colour of the object and to put the Specular colour to white. The emission colour is generally black for objects that do not shine by their own light.

Before you can use an OpenGL light source, it must be positioned using the glLight command and enabled using glEnable(GL_LIGHTn) where 'n' is 0 through 7. There are additional commands to make light sources directional (like a spotlight or a flashlight) and to have it attenuate as a function of range from the light source.

glColorMaterial

This is without doubt the most confusing thing about OpenGL lighting - and the biggest cause of problems for beginners.

The problem with using glMaterial to change polygon colours is three-fold:

  • It's slow. Well, the OpenGL manual says it's slow - but it's not certain that all implementations will have trouble with it.
  • You frequently need to change glMaterial properties for both Ambient and Diffuse to identical values - this takes two OpenGL function calls which is annoying.
  • You cannot change glMaterial settings with many of the more advanced polygon rendering techniques such as Vertex arrays and glDrawElements.
For these reasons, OpenGL has a feature that allows you do drive the glMaterial colours using the more flexible glColor command (which is not otherwise useful when lighting is enabled).

To drive (say) the Emission component of the glMaterial using glColor, you must say:

   glColorMaterial ( GL_FRONT_AND_BACK, GL_EMISSION ) ;
glEnable ( GL_COLOR_MATERIAL ) ;

From this point performing a glColor command (or setting the glColor via a vertex array or something) has the exact same effect as calling:
   glMaterial ( GL_FRONT_AND_BACK, GL_EMISSION, ...colours... ) ;

One especially useful option is:
   glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;

This causes glColor commands to change both Ambient and Diffuse colours at the same time. That's a very common thing to want to do for real-world lighting models.

Light Sources.

OpenGL's lights are turned on and off with glEnable(GL_LIGHT_n) and glDisable(GL_LIGHT_n) where 'n' is a number in the range zero to the maximum number of lights that this implementation supports (typically eight). The glLight call allows you to specify the colour (ambient, diffuse and specular), position, direction, beam width and attenuation rate for each light. By default, it is assumed that both the light and the viewer are effectively infinitely far from the object being lit. You can change that with the glLightModel call - but doing so is likely to slow down your program - so don't do it unless you have to. glLightModel also allows you to set a global ambient lighting level that's independent of the other OpenGL light sources.

There is also an option to light the front and back faces of your polygons differently. That is also likely to slow your program down - so don't do it.

glNormal

When lighting is enabled, OpenGL suddenly needs to know the orientation of the surface at each polygon vertex. You need to call glNormal for each vertex to define that - OpenGL does not provide a useful default.

Good Settings.

With this huge range of options, it can be hard to pick sensible default values for these things.

My advice for a starting point is to:

  • Set GL_LIGHT_0's position to something like 45 degrees to the 'vertical'. Coordinate (1,1,0) should work nicely in most cases.
  • Set GL_LIGHT_0's Ambient color to 0,0,0,1
  • Set GL_LIGHT_0's Diffuse color to 1,1,1,1
  • Set GL_LIGHT_0's Specular color to 1,1,1,1
  • Set the glLightModel's global ambient to 0.2,0.2,0.2,1 (this is the default).
  • Don't set any other glLight or glLightModel options - just let them default.
  • Enable GL_LIGHTING and GL_LIGHT_0.
  • Enable GL_COLOR_MATERIAL and set glColorMaterial to GL_AMBIENT_AND_DIFFUSE. This means that glMaterial will control the polygon's specular and emission colours and the ambient and diffuse will both be set using glColor.
  • Set the glMaterial's Specular colour to 1,1,1,1
  • Set the glMaterial's Emission colour to 0,0,0,1
  • Set the glColor to whatever colour you want each polygon to basically appear to be. That sets the Ambient and Diffuse to the same value which is what you generally want.

Using Alpha with lighting enabled.

One confusing thing is that each of the colour components (Ambient,Diffuse, Specular and Emission) has an associated 'alpha' component for setting transparency. It is important to know that only the DIFFUSE colour's alpha value actually determines the transparency of the polygon. If you have taken my advice and used glColorMaterial to cause glColor to drive the ambient and diffuse components then this seems perfectly natural...but people sometimes want to use the glColor to drive one of the other material components and are then very confused about their inability to set the transparency using glColor. If that happens to you - remember to use glMaterial to set the diffuse colour - and hence the alpha for the polygon overall.

5 Common C++ Programming Mistakes

It is normal for programmers to make mistakes while learning c++ programming. Mistakes (or errors) are often caused by misconception in the programmers mind or by forgetting certain things. While syntax errors are easier to spot since the c++ compiler shows the related information but logical errors often remains unnoticed, and many a times the program works just as fine. It is not possible to completely eliminate errors by yourself but it is definitely possible to minimize them as a c++ programmer. Here I am listing 5 of the common mistakes (errors) that
beginners commit (not sorted in any order).

Mistake No. 1

Have a look at this C++ Program

   #include

void main(void)

{

int base,height,area;

cout<<"Enter the base and height of the triangle:"; cin>>base>>height;

area=0.5*base*height;

cout<<<"AREA:"<
}

Here the variable area is such that sometimes its value would be whole number and sometimes it would be fractional. As long as we are giving even data the program would output correctly, but giving odd data would result in the loss of data in the output, since the integer data type can’t hold fractional values.

Mistake No.2

  #include

void main(void)

{

char name[15]=’c++ program’;

cout<
}

Here the problem is in the line char name[15]=’c++ program’; since we are giving string data as character constant. It should have been like this char name[15]=”c++ program”;

Mistake No. 3

   #include

void main(void)

{

int a=10,b=20,c;

a+b=c;

cout<
}


Can you spot the error, yes it is with the line a+b=c; here mathematical operations are being done in the left hand side and it is being assigned the value of the variable c, which is not possible. It should have been c=a+b;

Mistake No.4

   #include

void main(void)

{

int Name;

cin>>name;

cout<<"The value of Namee is:"<
}

I have seen many peoples committing this mistake, remember that C++ is Case-Sensitive, what it means is Name and name are two different identifiers for C++.

Mistake No. 5

   #include

void main(void)

{

int arr[3];

cout<<"Enter three numbers:";

cin>>arr[1]>>arr[2]>>arr[3];
   cout<<"The numbers are:"<
cout<<<
}

This is also one of the most common mistakes that c++ programmers commit. As I have previously discussed in the other articles that declaring an array as int arr[3]; means that it is for storing 3 elements of data type integer. And while accessing or modifying the index number starts with 0 and ends with one less than the total number of elements it can store.

Hope this article helps…


Data types that ranges ullor Microsoft Access, MySQL and SQL Server

Microsoft Access Data Types

Data type Description Storage
Text Use for text or combinations of text and numbers. 255 characters maximum
Memo Memo is used for larger amounts of text. Stores up to 65,536 characters. Note: You cannot sort a memo field. However, they are searchable
Byte Allows whole numbers from 0 to 255 1 byte
Integer Allows whole numbers between -32,768 and 32,767 2 bytes
Long Allows whole numbers between -2,147,483,648 and 2,147,483,647 4 bytes
Single Single precision floating-point. Will handle most decimals 4 bytes
Double Double precision floating-point. Will handle most decimals 8 bytes
Currency Use for currency. Holds up to 15 digits of whole dollars, plus 4 decimal places. Tip: You can choose which country's currency to use 8 bytes
AutoNumber AutoNumber fields automatically give each record its own number, usually starting at 1 4 bytes
Date/Time Use for dates and times 8 bytes
Yes/No A logical field can be displayed as Yes/No, True/False, or On/Off. In code, use the constants True and False (equivalent to -1 and 0). Note: Null values are not allowed in Yes/No fields 1 bit
Ole Object Can store pictures, audio, video, or other BLOBs (Binary Large OBjects) up to 1GB
Hyperlink Contain links to other files, including web pages
Lookup Wizard Let you type a list of options, which can then be chosen from a drop-down list 4 bytes


MySQL Data Types

In MySQL there are three main types : text, number, and Date/Time types.

Text types:

Data type Description
CHAR(size) Holds a fixed length string (can contain letters, numbers, and special characters). The fixed size is specified in parenthesis. Can store up to 255 characters
VARCHAR(size) Holds a variable length string (can contain letters, numbers, and special characters). The maximum size is specified in parenthesis. Can store up to 255 characters. Note: If you put a greater value than 255 it will be converted to a TEXT type
TINYTEXT Holds a string with a maximum length of 255 characters
TEXT Holds a string with a maximum length of 65,535 characters
BLOB For BLOBs (Binary Large OBjects). Holds up to 65,535 bytes of data
MEDIUMTEXT Holds a string with a maximum length of 16,777,215 characters
MEDIUMBLOB For BLOBs (Binary Large OBjects). Holds up to 16,777,215 bytes of data
LONGTEXT Holds a string with a maximum length of 4,294,967,295 characters
LONGBLOB For BLOBs (Binary Large OBjects). Holds up to 4,294,967,295 bytes of data
ENUM(x,y,z,etc.) Let you enter a list of possible values. You can list up to 65535 values in an ENUM list. If a value is inserted that is not in the list, a blank value will be inserted.

Note: The values are sorted in the order you enter them.

You enter the possible values in this format: ENUM('X','Y','Z')

SET Similar to ENUM except that SET may contain up to 64 list items and can store more than one choice

Number types:

Data type Description
TINYINT(size) -128 to 127 normal. 0 to 255 UNSIGNED*. The maximum number of digits may be specified in parenthesis
SMALLINT(size) -32768 to 32767 normal. 0 to 65535 UNSIGNED*. The maximum number of digits may be specified in parenthesis
MEDIUMINT(size) -8388608 to 8388607 normal. 0 to 16777215 UNSIGNED*. The maximum number of digits may be specified in parenthesis
INT(size) -2147483648 to 2147483647 normal. 0 to 4294967295 UNSIGNED*. The maximum number of digits may be specified in parenthesis
BIGINT(size) -9223372036854775808 to 9223372036854775807 normal. 0 to 18446744073709551615 UNSIGNED*. The maximum number of digits may be specified in parenthesis
FLOAT(size,d) A small number with a floating decimal point. The maximum number of digits may be specified in the size parameter. The maximum number of digits to the right of the decimal point is specified in the d parameter
DOUBLE(size,d) A large number with a floating decimal point. The maximum number of digits may be specified in the size parameter. The maximum number of digits to the right of the decimal point is specified in the d parameter
DECIMAL(size,d) A DOUBLE stored as a string , allowing for a fixed decimal point. The maximum number of digits may be specified in the size parameter. The maximum number of digits to the right of the decimal point is specified in the d parameter

*The integer types have an extra option called UNSIGNED. Normally, the integer goes from an negative to positive value. Adding the UNSIGNED attribute will move that range up so it starts at zero instead of a negative number.

Date types:

Data type Description
DATE() A date. Format: YYYY-MM-DD

Note: The supported range is from '1000-01-01' to '9999-12-31'

DATETIME() *A date and time combination. Format: YYYY-MM-DD HH:MM:SS

Note: The supported range is from '1000-01-01 00:00:00' to '9999-12-31 23:59:59'

TIMESTAMP() *A timestamp. TIMESTAMP values are stored as the number of seconds since the Unix epoch ('1970-01-01 00:00:00' UTC). Format: YYYY-MM-DD HH:MM:SS

Note: The supported range is from '1970-01-01 00:00:01' UTC to '2038-01-09 03:14:07' UTC

TIME() A time. Format: HH:MM:SS

Note: The supported range is from '-838:59:59' to '838:59:59'

YEAR() A year in two-digit or four-digit format.

Note: Values allowed in four-digit format: 1901 to 2155. Values allowed in two-digit format: 70 to 69, representing years from 1970 to 2069

*Even if DATETIME and TIMESTAMP return the same format, they work very differently. In an INSERT or UPDATE query, the TIMESTAMP automatically set itself to the current date and time. TIMESTAMP also accepts various formats, like YYYYMMDDHHMMSS, YYMMDDHHMMSS, YYYYMMDD, or YYMMDD.


SQL Server Data Types

Character strings:

Data type Description Storage
char(n) Fixed-length character string. Maximum 8,000 characters n
varchar(n) Variable-length character string. Maximum 8,000 characters
varchar(max) Variable-length character string. Maximum 1,073,741,824 characters
text Variable-length character string. Maximum 2GB of text data

Unicode strings:

Data type Description Storage
nchar(n) Fixed-length Unicode data. Maximum 4,000 characters
nvarchar(n) Variable-length Unicode data. Maximum 4,000 characters
nvarchar(max) Variable-length Unicode data. Maximum 536,870,912 characters
ntext Variable-length Unicode data. Maximum 2GB of text data

Binary types:

Data type Description Storage
bit Allows 0, 1, or NULL
binary(n) Fixed-length binary data. Maximum 8,000 bytes
varbinary(n) Variable-length binary data. Maximum 8,000 bytes
varbinary(max) Variable-length binary data. Maximum 2GB
image Variable-length binary data. Maximum 2GB

Number types:

Data type Description Storage
tinyint Allows whole numbers from 0 to 255 1 byte
smallint Allows whole numbers between -32,768 and 32,767 2 bytes
int Allows whole numbers between -2,147,483,648 and 2,147,483,647 4 bytes
bigint Allows whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 8 bytes
decimal(p,s) Fixed precision and scale numbers.

Allows numbers from -10^38 +1 to 10^38 –1.

The p parameter indicates the maximum total number of digits that can be stored (both to the left and to the right of the decimal point). p must be a value from 1 to 38. Default is 18.

The s parameter indicates the maximum number of digits stored to the right of the decimal point. s must be a value from 0 to p. Default value is 0

5-17 bytes
numeric(p,s) Fixed precision and scale numbers.

Allows numbers from -10^38 +1 to 10^38 –1.

The p parameter indicates the maximum total number of digits that can be stored (both to the left and to the right of the decimal point). p must be a value from 1 to 38. Default is 18.

The s parameter indicates the maximum number of digits stored to the right of the decimal point. s must be a value from 0 to p. Default value is 0

5-17 bytes
smallmoney Monetary data from -214,748.3648 to 214,748.3647 4 bytes
money Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 8 bytes
float(n) Floating precision number data from -1.79E + 308 to 1.79E + 308.

The n parameter indicates whether the field should hold 4 or 8 bytes. float(24) holds a 4-byte field and float(53) holds an 8-byte field. Default value of n is 53.

4 or 8 bytes
real Floating precision number data from -3.40E + 38 to 3.40E + 38 4 bytes

Date types:

Data type Description Storage
datetime From January 1, 1753 to December 31, 9999 with an accuracy of 3.33 milliseconds 8 bytes
datetime2 From January 1, 0001 and December 31, 9999 with an accuracy of 100 nanoseconds 6-8 bytes
smalldatetime From January 1, 1900 to June 6, 2079 with an accuracy of 1 minute 4 bytes
date Store a date only. From January 1, 0001 to December 31, 9999 3 bytes
time Store a time only to an accuracy of 100 nanoseconds 3-5 bytes
datetimeoffset The same as datetime2 with the addition of a time zone offset 8-10 bytes
timestamp Stores a unique number that gets updated every time a row gets created or modified. The timestamp value is based upon an internal clock and does not correspond to real time. Each table may have only one timestamp variable

Other data types:

Data type Description
sql_variant Stores up to 8,000 bytes of data of various data types, except text, ntext, and timestamp
uniqueidentifier Stores a globally unique identifier (GUID)
xml Stores XML formatted data. Maximum 2GB
cursor Stores a reference to a cursor used for database operations
table Stores a result-set for later processing

Wednesday, April 29, 2009

How to parse an XML file in J2ME with kXML

Source code: XmlNode class

First, we'll define an XmlNode class to represent a generic XmlNode. We'll define 2 node types:

  • Text nodes: nodes without a name, containing only a textual value
  • Element nodes: nodes with a tag name, containing children nodes

Each node will have also a list of attributes.

public class XmlNode
{
public static final int TEXT_NODE = 0;
public static final int ELEMENT_NODE = 1;

public int nodeType = 0;
public String nodeName = null;
public String nodeValue = null;
public Vector children = null;
public Hashtable attributes = null;

public XmlNode(int nodeType)
{
this.nodeType = nodeType;
this.children = new Vector();
this.attributes = new Hashtable();
}
public String[] getAttributeNames()
{
String[] names = new String[attributes.size()];

Enumeration e = attributes.keys();

int i = 0;

while(e.hasMoreElements())
{
names[i] = (String)e.nextElement();

i++;
}
return names;
}
public void setAttribute(String key, String value)
{
attributes.put(key, value);
}
public String getAttribute(String key)
{
return (String)attributes.get(key);
}

public void addChild(XmlNode child)
{
this.children.addElement(child);
}
}

Source code: GenericXmlParser class

GenericXmlParser is the class with which we'll able to parse XML files. It has only one public method, parseXML(), that will accept as arguments:

  • a KXmlParser instance to do parsing
  • a boolean that defines if whitespaces-only children must be ignored
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;

public class GenericXmlParser
{
public XmlNode parseXML(KXmlParser parser,
boolean ignoreWhitespaces) throws Exception
{
parser.next();

return _parse(parser, ignoreWhitespaces);
}
XmlNode _parse(KXmlParser parser,
boolean ignoreWhitespaces) throws Exception
{
XmlNode node = new XmlNode(XmlNode.ELEMENT_NODE);

if(parser.getEventType() != XmlPullParser.START_TAG)
{
throw new Exception("Illegal XML state: " +
parser.getName() + ", " + parser.getEventType());
}
else
{
node.nodeName = parser.getName();

for(int i = 0; i < style="color: rgb(0, 102, 0);">getAttributeCount(); i++)
{
node.setAttribute(parser.getAttributeName(i), parser.getAttributeValue(i));
}

parser.next();

while(parser.getEventType() != XmlPullParser.END_TAG)
{
if(parser.getEventType() == XmlPullParser.START_TAG)
{
node.addChild(_parse(parser, ignoreWhitespaces));
}
else if(parser.getEventType() == XmlPullParser.TEXT)
{
if(!ignoreWhitespaces || !parser.isWhitespace())
{
XmlNode child = new XmlNode(XmlNode.TEXT_NODE);

child.nodeValue = parser.getText();

node.addChild(child);
}
}
parser.next();
}
}
return node;
}
}

Source code: sample usage

To use the classes defined above, you can simply do as follows:

InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/test3.xml"));

KXmlParser parser = new KXmlParser();

parser.setInput(reader);

GenericXmlParser gParser = new GenericXmlParser();

XmlNode xml = gParser.parseXML(parser, true);

And, if you want to dump out the parsed XML data, you can use the following method:

void dumpXML(XmlNode node, int deep)
{
for(int i = 0; i < style="color: rgb(102, 204, 102);">)
{
System.out.print(" ");
}
System.out.print(node.nodeName + " - ");

if(node.nodeValue != null)
{
System.out.print("(" + node.nodeValue + ") - ");
}
String[] attributes = node.getAttributeNames();

for(int i = 0; i < style="color: rgb(0, 102, 0);">length; i++)
{
System.out.print(attributes[i] + ": " + node.getAttribute(attributes[i]) + ", ");
}

System.out.println();

for(int i = 0; i < style="color: rgb(0, 102, 0);">children.size(); i++)
{
dumpXML((XmlNode)node.children.elementAt(i), deep + 1);
}
}

Related Discussions

C# Encapsulation Lesson

Now that you've seen much of the syntax of C#, I'll show you how C# supports the another of the object-oriented principles - Encapsulation. This lesson will discuss Encapsulation with the following objectives:

  • Understand the object-oriented principle of Encapsulation.
  • Learn the available modifiers for type members.
  • Protect object state through properties.
  • Control access to methods.
  • Learn how to modify types for assembly encapsulation

What is Encapsulation and How Does It Benefit Me?

In object-oriented programming, you create objects that have state and behavior. An object's state is the data or information it contains. For example, if you have a BankAccount object, its state could be Amount and CustomerName. Behavior in an object is often represented by methods. For example, the BankAccount object's behavior could be Credit, Debit, and GetAmount. This sounds like a nice definition of an object, and it is, but you must also consider how this object will be used.

When designing an object, you must think about how others could use it. In a best-case scenario any program using the object would be well designed and the code would never change. However, the reality is that programs do change often and in a team environment many people touch the same code at one time or another. Therefore, it is beneficial to consider what could go wrong as well as the pristine image of how the object *should* be used.

In the case of the BankAccount object, examine the situation where code outside of your object could access a decimal Amount field or a string CustomerName field. At the point of time that the code is written, everything would work well. However, later in the development cycle, you realize that the BankAccount object should keep track of an int CustomerID rather than string CustomerName because you don't want to duplicate relationships between information (or some other valid reason to alter the definition of internal state). Such changes cause a rippling effect in your code because it was built to use the BankAccount class, as originally designed (with CustomerName being a string), and you must now change code that accesses that state throughout your entire application.

The object-oriented principle of Encapsulation helps avoid such problems, allowing you to hide internal state and abstract access to it though type members such as methods, properties, and indexers. Encapsulation helps you reduce coupling between objects and increases the maintainability of your code.

Type Member Access Modifiers

An access modifier allows you to specify the visibility of code outside a type or assembly. Access modifiers can be applied to either types or type members. A later section on Type Access Modifiers discusses modifiers that can be applied to types. This section discusses those modifiers that apply to type members and how they affect visibility.

Generally, you should hide the internal state of your object from direct access from outside code. Then implement other members, such as methods and properties, that wrap that state. This allows the internal implementation of the state to change at will, while the members wrapping the state can still return a representation of the state that doesn't change. This means that outside code will access your object via members that wrap state and be guaranteed that the type of information extracted is consistent. Additionally, because external code doesn't have access to the internal state of your object, they can't alter that state in an inconsistent manner that could break the way your object works.

The first step in encapsulating object state is to determine what type of access that outside code should have to the members of your type. This is performed with access modifiers. The type of access granted varies from no external access at all to full public access and a few variations in between the extremes. table 19-1 lists all of the type member access modifiers and explains their meaning.

table 19-1. Type member access modifiers control what code has access to a specified type member.

Access Modifier Description (who can access)
private Only members within the same type. (default for type members)
protected Only derived types or members of the same type.
internal Only code within the same assembly. Can also be code external to object as long as it is in the same assembly. (default for types)
protected internal Either code from derived type or code in the same assembly. Combination of protected OR internal.
public Any code. No inheritance, external type, or external assembly restrictions.

As you've learned from previous lessons of the C# Tutorial, types contain several types of members, including constructors, properties, indexers, methods, and others. Rather than show you an exhaustive list of all of the permutations of access modifiers you can use with these members, I'll take a more practical approach and describe a sub-set of access modifiers used on properties and methods.

Opening Type Members to public Access

You've seen the public access modifier used in earlier parts of the C# Tutorial. Any time the public access modifier is used on a type member, calling code will be able to access the type member. If you make your type member public, you are giving everyone permission to use it. Listing 19-1 shows an example of using the public access modifier on a method.

Listing 19-1. Declaring a Method with a public Access Modifier: BankAccountPublic.cs

using System;

class BankAccountPublic
{
public decimal GetAmount()
{
return 1000.00m;
}
}

The GetAmount() method in Listing 19-1 is public meaning that it can be called by code that is external to this class. Now, you can write the following code, elsewhere in your program, to use this method:

BankAccountPublic bankAcctPub = new BankAccountPublic();

// call a public method
decimal
amount = bankAcctPub.GetAmount();

All you need to do, as shown above, is create an instance of the class that contains the method and then call the method through that instance. Because it is public, you won't have a problem. Remember that the default access for a type member is private, which we'll talk about next. This means that if you forget the public modifier, and didn't use any modifier at all, you would receive a compiler error.

Hiding Type Members with private Access

A private type member is one that can only be accessed by members within the same type. For example, if the BankAccount class has a private member, only other members of the BankAccount class can access or call that member.

Although the default access for type members is private, I prefer to be explicit about my intentions when declaring type members and include the access modifier, rather than rely on defaults. I think it makes the code easier to read and makes it clear to other developers what my true intention is. Listing 19-2 shows how to use the private access modifier and offers an example of why you would want to use it.

Listing 19-2. Declaring a private Field: BankAccountPrivate.cs

using System;

class
BankAccountPrivate
{
private string m_name;

public string CustomerName
{
get { return m_name; }
set { m_name = value; }
}
}

It's common to encapsulate the state of your type with properties. In fact, I always wrap my type state in a property. In Listing 19-2, you can see how the name of the customer is held in the m_name field, but it is wrapped (encapsulated) with the CustomerName property. Because m_name is declared as private, code outside the BankAccountPrivate class can't access it directly. They must use the public CustomerName property instead.

Now you can change the implementation of m_name in any way you want. For example, what if you wanted it to be an ID of type int and the CustomerName property would do a search to find the name or what if you wanted to have first and last name values that the CustomerName property could concatenate. There are all kinds of things happening to your code in maintenance that will causes implementation to change. The point is that private members allow the implementation to change without constraining the implementation or causing rippling effects throughout your code base that would have occurred if that external code had access to the members of your type.

The private and public access modifiers are at the two extremes of access, either denying all external access or allowing all external access, respectively. The other access modifiers are like different shades of gray between these two extremes, including the protected modifier, discussed next.

Access for Derived Types with the protected Access Modifier

In some ways, the protected access modifier acts like both the private and public access modifiers. Like private, it only allows access to members within the same type, except that it acts like public only to derived types. Said another way, protected type members can only be accessed by either members within the same type or members of derived types.

Returning to the BankAccount example, what if you needed to call code to close an account? Furthermore, what if there were different types of accounts? Each of these different account types would have their own logic for closing, but the basic process would be the same for all account types. If this sounds to you like the description of Polymorphism, you would be on the right track. Back in Lesson 9, we discussed polymorphism and how it allows us to treat different classes the same way. You may want to refer to Lesson 9 for a refresher before looking at the next example.

In the case of closing an account, there are several things that need to be done like calculating interest that is due, applying penalties for early withdrawal, and doing the work to remove the account from the database. Individually, you don't want any code to call methods of the BankAccount class unless all of the methods are called and each method is called in the right order. For example, what if some code called the method to delete the account from the database and didn't calculate interest or apply penalties? Someone would loose money. Also, if the calling code were to delete the account first then the other methods wouldn't run into errors because the account information isn't available. Therefore, you need to control this situation and Listing 19-3 shows how you can do it.

Listing 19-3. Declaring protected Methods: BankAccountProtected.cs

using System;

class
BankAccountProtected
{
public void CloseAccount()
{
ApplyPenalties();
CalculateFinalInterest();
DeleteAccountFromDB();
}

protected virtual void ApplyPenalties()
{
// deduct from account
}

protected virtual void CalculateFinalInterest()
{
// add to account
}

protected virtual void DeleteAccountFromDB()
{
// send notification to data entry personnel
}
}

The most important parts of Listing 19-3 are that the CloseAccount method is public and the other methods are protected. Any calling code can instantiate BankAccountProtected, but it can only call the CloseAccount method. This gives you protection from someone invoking the behavior of your object in inappropriate ways. Your business logic is sound.

At the end of this section, you'll see an example of how to call the code in Listing 19-3. For now, it is essential that you see how the other pieces fit together first.

If you only wanted the BankAccountProtected class to operate on its own members, you could have made the protected methods private instead. However, this code supports a framework where you can have different account types such as Savings, Checking, and more. You will be able to add new account types in the future because the BankAccountProtected class is designed to support them with protected virtual methods. Listings 19-4 and 19-5 show you the SavingsAccount and CheckingAccount classes that derive from the BankAccountProtected class.

Listing 19-4. Derived SavingsAccount Class Using protected Members of its Base Class: SavingsAccount.cs

using System;

class
SavingsAccount : BankAccountProtected
{
protected override void ApplyPenalties()
{
Console.WriteLine("Savings Account Applying Penalties");
}

protected override void CalculateFinalInterest()
{
Console.WriteLine("Savings Account Calculating Final Interest");
}

protected override void DeleteAccountFromDB()
{
base.DeleteAccountFromDB();

Console.WriteLine("Savings Account Deleting Account from DB");
}
}

Notice how SavingsAccount derives from BankAccountProtected. SavingsAccount can access any of the protected members of the BankAccountProtected class which is its base class. It demonstrates this fact via the call to base.DeleteAccountFromDB in it's DeleteAccountFromDB method. If the inheritance part of Listing 19-4 is a little confusing, you can visit Lesson 8: Class Inheritance for a refresher and better understanding. Each method of SavingsAccount has the protected access modifier also, which simply means that classes derived from SavingsAccount can access those SavingsAccount members with the protected access modifier. The same situation exists with the CheckingAccount class, shown in Listing 19-5.

Listing 19-5. Derived CheckingAccount Class Using protected Members of its Base Class: CheckingAccount.cs

using System;

class
CheckingAccount : BankAccountProtected
{
protected override void ApplyPenalties()
{
Console.WriteLine("Checking Account Applying Penalties");
}

protected override void CalculateFinalInterest()
{
Console.WriteLine("Checking Account Calculating Final Interest");
}

protected override void DeleteAccountFromDB()
{
base.DeleteAccountFromDB();
Console.WriteLine("Checking Account Deleting Account from DB");
}
}

The CheckingAccount class in Listing 19-5 is implemented similar to SavingsAccount from Listing 19-6. If you were writing this, the difference would be that the methods of each class would have unique implementations. For example, the business rules associated with the final interest calculation would differ, depending on whether the account type was checking or savings.

Notice the call to the base class method in the DeleteAccountFromlDB method in CheckingAccount. Just like SavingsAccount, CheckingAccount has access to BankAccountProtected's protected method because it is a derived class. This is a common pattern in polymorphism because derived classes often have a responsibility to call virtual base class methods to ensure critical functionality has the opportunity to execute. You would consult the method documentation to see if this was necessary. Without a protected access modifier, your only option would have been to make the base class method public; which, as explained earlier, is dangerous.

To use the code from Listings 19-3, 19-4, and 19-5, you can implement the following code:

BankAccountProtected[] bankAccts = new BankAccountProtected[2];
bankAccts[0] =
new SavingsAccount();
bankAccts[1] =
new CheckingAccount();

foreach (BankAccountProtected acct in bankAccts)
{
// call public method, which invokes protected virtual methods
acct.CloseAccount();
}

Since both SavingsAccount and CheckingAccount derive from BankAccountProtected, you can assign them to the bankAccts array. They both override the protected virtual methods of BankAccountProtected, so it is the SavingsAccount and CheckingAccount methods that are called when CloseAccount in BankAccountProtected executes. Remember that the only reason the methods of SavingsAccount and CheckingAccount can call their virtual base class methods, as in the case of DeleteAccountFromDB, is because the virtual base class methods are marked with the protected access modifier.

A Quick Word on internal and protected internal Access Modifiers

In practice, most of the code you write will involve the public, private, and protected access modifiers. However, there are two more access modifiers that you can use in more sophisticated scenarios: internal and protected internal.

You would use internal whenever you created a separate class library and you don't want any code outside of the library to access the code with internal access. The protected internal is a combination of the two access modifiers it is named after, which means either protected or internal.

Access Modifiers for Types

So far, the discussion of access modifiers has only applied to the members of types. However, the rules are different for the types themselves. When talking about types, I'm referring to all of the C# types, including classes, structs, interfaces, delegates, and enums. Nested types, such as a class defined within the scope of a class, are considered type members and fall under the same access rules as other type members.

Types can have only two access modifiers: public or internal. The default, if you don't specify the access modifier, is internal. Looking at all of the classes used in this lesson, you can see that they are internal because they don't have an access modifier. You can explicitly specify internal like this:

internal class InternalInterestCalculator
{
// members go here
}

Perhaps the InternalInterestCalculator, shown above, has special business rules that you don't want other code to use. Now, it is in a class library of its own and can only be accessed by other code inside of that same class library (DLL).

Note: To be more specific, internal means that only code in the same assembly can access code marked as internal. However, discussing the definition of an assembly is outside the scope of this lesson, so I am simplifying the terminology.

If you declared a class inside of a class library that you wanted other code to use, you would give it a public access modifier. The following code shows an example of applying the public access modifier to a type:

public class BankAccountExternal
{
// members go here
}

Clearly, a bank account is something you would want to access from outside of a class library. Therefore, it only makes sense to give it a public access modifier as shown in the BankAccountExternal class above.