J2ME Polish
J2ME Polish 2.4 Documentation
Enough Software

Preprocessing Directives

Preprocessing directives always start with a "//#" and often have one or more arguments. All directives must not span several rows. J2ME Polish supports all directives of the antenna preprocessor (antenna.sourceforge.net), by the way. So if you migrate from antenna, you can keep your preprocessing directives.

Checking a Single Symbol with #ifdef, #ifndef, #else, #elifdef, #elifndef and #endif

Single symbols can be checked easily with the #ifdef directive:

//#ifdef polish.images.directLoad
	Image image = Image.createImage( name );
	//# return image;
//#else
	scheduleImage( name );
	return null;
//#endif

When the symbol "polish.images.directLoad" is defined, the code will be transformed to the following:

//#ifdef polish.images.directLoad
	Image image = Image.createImage( name );
	return image;
//#else
	//# scheduleImage( name );
	//# return null;
//#endif

If, however, the symbol "polish.images.directLoad" is not defined, the transformation will be:

//#ifdef polish.images.directLoad
	//# Image image = Image.createImage( name );
	//# return image;
//#else
	scheduleImage( name );
	return null;
//#endif

Each #ifdef and #ifndef directive needs to be closed by the #endif directive. If a variable is defined, it can be checked via //#ifdef [variable]:defined:

//#ifdef polish.ScreenSize:defined
Directive  Meaning  Explanation
//#ifdef [symbol] if [symbol] is defined The symbol named [symbol] needs to be defined, when the next section should be compiled.
//#ifndef [symbol] if [symbol] is not defined The symbol named [symbol] must not be defined, when the next section should be compiled.
//#else else When the previous section was false, the following section will be compiled (and the other way round).
//#elifdef [symbol] else if [symbol] is defined The symbol named [symbol] needs to be defined and the previous section needs to be false, when the next section should be compiled.
//#elifndef [symbol] else if [symbol] is not defined The symbol named [symbol] must not be defined and the previous section needs to be false, when the next section should be compiled.
//#endif end of the if-block End of every ifdef and ifndef block.

The #ifdef directives can be nested, of course. Other preprocessing directives can be included into the sub-sections as well:

//#ifdef mySymbol
	//#ifndef myOtherSymbol
		//#debug
		System.out.println("only mySymbol is defined.");
		doSomething();
	//#else
		//#debug
		System.out.println("mySymbol and myOtherSymbol are defined.");
		doSomethingElse();
	//#endif
//#endif

The #ifdef directive and its related directives are faster to process than the more complex #if directives.

Check Several Symbols and Variables with #if, #else, #elif and #endif

With each #ifdef directive only a single symbol can be checked. With the #if and #elif directives, however, complex terms containing several symbols and variables can be evaluated:

//#if useEnhancedInput && (polish.hasPointerEvents || polish.mouseSupported)
	doSomething();
//#endif

#if directives can also be nested and contain other preprocessing directives like the #ifdef directives. #if and #ifdef directives can also be mixed:

//#if !basicInput && (polish.hasPointerEvents || polish.mouseSupported)
	doSomething();
	//#if polish.BitsPerPixel >= 8
		doSomethingColorful();
	//#else
		doSomethingDull();
	//#endif
//#elifdef doWildStuff
	doWildStuff();
//#endif
Directive  Meaning  Explanation
//#if [term] if [term] is true The specified term must be true, when the next section should be compiled.
//#else else When the previous section was false, the following section will be compiled (and the other way round).
//#elif [term] else if [term] is true The specified term needs to be true and the previous section needs to be false, when the next section should be compiled.
//#endif end of the if-block End of every if block.

In the terms the boolean operators &&, ||, ^ and ! can be used. Complex terms can be separated using normal parentheses "(" and ")". The term arguments for boolean operators are symbols, which are true when they are defined and false otherwise.

Variables can be checked with the comparator ==, >, <, <= and >=. Arguments for the comparators are variables or constants.

A term can include comparators and boolean operators, when the sections are separated by parentheses.

Boolean Operator  Meaning  Explanation
&& or "and" and Both arguments/symbols need to be defined:
true && true = true
true && false = false && true = false && false = false
|| or "or" or At least one argument/symbol must be defined:
true || true = true || false = false || true = true
false || false = false
^ or "xor" xor Only and at least one argument/symbol must be defined:
true ^ false = false ^ true = true
true ^ true = false ^ false = false
! or "not" not The argument/symbol must not be defined:
! false = true
! true = false
Comparator  Meaning  Explanation
== equals The left and the right argument must be equal, integers and strings can be compared:
8 == 8 = true
Nokia == Nokia = true
//#if polish.BitsPerPixel == 8
//#if polish.vendor == Nokia
!= not equals The left and the right argument must not be equal, integers and strings can be compared:
8 != 8 = false
Nokia != Sony-Ericsson = true
//#if polish.BitsPerPixel != 8
//#if polish.vendor != Nokia
> greater The left argument must be greater than the right one. Only integers and versions can be compared:
8 > 8 = false
16 > 8 = true
//#if polish.BitsPerPixel > 8
//#if polish.JavaPlatform > BlackBerry/6.0
< smaller The left argument must be smaller than the right one. Only integers and versions can be compared:
8 < 8 = false
8 < 16 = true
//#if polish.BitsPerPixel < 8
//#if polish.JavaPlatform < BlackBerry/6.0
>= greater or equals The left argument must be greater than or equals the right one. Only integers and versions can be compared:
8 >= 8 = true
16 >= 8 = true
//#if polish.BitsPerPixel >= 8
//#if polish.JavaPlatform >= BlackBerry/6.0
<= smaller or equals The left argument must be smaller than or equals the right one. Only integers and versions can be compared:
8 <= 8 = true
8 <= 16 = true
//#if polish.BitsPerPixel <= 8
//#if polish.JavaPlatform <= BlackBerry/6.0

Using Variable-Functions for Comparing Values

Some variables have not only different values but also store them in different ways. An example are memory values like the HeapSize which is sometimes defined in kilobytes and sometimes in megabytes. In such situation functions can be used:

//#if ${ bytes( polish.HeapSize ) } > 102400

In the above example the heapsize is compared using the bytes-function. When a function should be used, a dollar-sign and curly parentheses needs to embrace the call. The bytes-function will return -1 when the given memory-value is "dynamic", so it might be necessary to test for both values:

//#if ( ${ bytes( polish.HeapSize ) } > 102400 ) or (polish.HeapSize == dynamic)

Functions can also be used for fixed values:

//#if ${ bytes( polish.HeapSize ) } > ${ bytes( 100 kb ) }

You will find more information about using functions at the "Using Variables" section.

Hiding Code

Code sections can be temporarily commented out, to avoid problems in the IDE. A typical problem are several return statements:

//#ifdef polish.images.directLoad
	Image image = Image.createImage( name );
	//# return image;
//#else
	scheduleImage( name );
	return null;
//#endif

In this example the first return statement is hidden with a "//# " directive. When the first #ifdef directive is true, the corresponding code will be made visible again. The space after the # sign is important for the correct handling of such comments.

Debugging with #debug, #mdebug and #enddebug

To include debugging information in a J2ME application is not without problems. The main problem is that any debugging slows down an application unnecessarily. Another problem is that nobody wants the debugging information in an application which should be deployed "in the wild". With J2ME Polish debugging statements can be included into the applications without having the above disadvantages. The #debug directive is used to include a single line of debugging information:

//#debug
System.out.println("debugging is enabled for this class.");

You can define what debugging level is used by adding the debugging-level to the #debug directive. When no level is specified the "debug" level is assumed.

//#debug info
System.out.println("the info debugging level is enabled for this class.");

The #mdebug (multi-line debug) directive can be used to use multiple lines of debugging information. It must be finished with the #enddebug directive:

//#mdebug error
e.printStackTrace();
System.out.println("unable to load something: " + e.getMessage() );
//#enddebug
Directive  Explanation
//#debug [level] The next line is only compiled when the debugging setting for the class in which the debugging information is enabled for the specified level. When no level is specified, the "debug" level is assumed.
//#mdebug [level] The next lines up to the #enddebug directive will be compiled only when the debugging setting for the class in which the debugging information is enabled for the specified level. When no level is specified, the "debug" level is assumed.
//#enddebug Marks the end of the #mdebug section.

Please read the logging documentation for further information.

Using Variables with #=

You can add the contents of variables with the #= directive:

//#= private String url = "${ start-url }";

When the variable is not defined, the above example would throw an exception during the preprocessing step of the build process. You can use the "[variable]:defined" symbol, which is set for every known variable:

//#ifdef start-url:defined
	//#= private String url = "${ start-url }";
//#else
	private String url = "http://192.168.101.101";
//#endif

This is especially useful for setting configuration values, which can change easily, like Web-URLs and so on.

The name of the variable needs to be surrounded by a Dollar-Sign followed by curly parentheses (${ variable-name }), just like referring to Ant-properties in the build.xml.

Variables can be defined in the <variables> element in the build.xml. Other variables are defined in the devices.xml, vendors.xml and groups.xml by the capabilities of the devices, vendors and groups. These files are located in the installation directory of J2ME Polish.

Variable-Function

You can use functions like "uppercase" to change the values. A function is used within the curly parentheses and surrounds the variable-name by normal parentheses:

//#ifdef start-url:defined
	//#= private String url = "${ lowercase(start-url) }";
//#else
	private String url = "http://192.168.101.101";
//#endif

When using function you do not necessarily need a variable - you can also use a direct value. This can be handy when handling with memory values for example:

//#if ${ bytes( polish.HeapSize ) } > ${ bytes(100 kb) }

Following functions can be used:

  • uppercase: Translates the given value into uppercase. "abc" becomes "ABC" for example.
  • lowercase: Translates the given value into lowercase. "ABC" becomes "abc" for example.
  • bytes: Calculates the number of bytes of the given memory value. "1 kb" becomes 1024 for example. The memory-value "dynamic" results in -1.
  • kilobytes: Calculates the (double) number of kilobytes of the given memory value. The value will contain a point and decimal places. "512 bytes" becomes "0.5" for example, "1024 bytes" becomes "1" and so on.
  • megabytes: Calculates the (double) number of megabytes of the given memory value.
  • gigabytes: Calculates the (double) number of gigabytes of the given memory value.

Setting CSS Styles with #style

CSS styles are used for the design of the application. The styles are defined in the file polish.css in the resources folder of the project. The developer can control which styles are used for specific Items by using the #style directive:

//#style cool, frosty, default
StringItem url = new StringItem( null, "http://192.168.101.101" );

In the above example one of the styles "cool", "frosty" or "default" is used for the new item. The style "frosty" is only used when the style "cool" is not defined. The style "default" is only used, when neither the style "cool" nor the style "frosty" is defined. The style "default" is special, since it is always defined, even when the designer does not define it explicitly. The #style directive needs at least one style name as argument. The style is deemed optional when the style ends with a question mark. In that case it will only be used when it is defined:

//#style cool?
StringItem url = new StringItem( null, "http://192.168.101.101" );

The styles mentioned here can be defined in the "resources/polish.css" file. In that file names of custom styles need to start with a dot. In the above example you can define the styles ".cool", ".frosty" or "default". The "default" style is a predefined style and its name must not, therefore, start with a dot.

Styles are only used, when the J2ME Polish GUI is used. This can be triggered with the "usePolishGui" attribute of the <build> element in the build.xml. Using #style directive improves the performance of the application, since dynamic styles need some processing time. Dynamic styles design items by their position in screens, see section "Dynamic Styles". Styles can be set for all items-constructors and for many methods:

Insertion Point  Example  Explanation
Item constructors //#style cool, frosty, default
StringItem url = new StringItem( null, "http://192.168.101.101" );
//#style cool
ImageItem img = new ImageItem( null, iconImage, ImageItem.LAYOUT_DEFAULT, null );
The #style directive can be placed before any item constructor.
Item. setAppearanceMode() //#style openLink
url.setAppearanceMode( Item.HYPERLINK );
The #style directive can be placed before calling the setAppearanceMode-method of an Item. Please note, that this method is only available in J2ME Polish.
List.append() //#style choice
list.append( "Start", null );
The #style directive can be placed before adding a list element.
List.insert() //#style choice
list.insert( 2, "Start", null );
The #style directive can be placed before inserting a list element.
List.set() //#style choice
list.set( 2, "Start", null );
The #style directive can be placed before setting a list element.
ChoiceGroup.append() //#style choice
group.append( "Choice 1", null );
The #style directive can be placed before adding an element to a ChoiceGroup.
ChoiceGroup.insert() //#style choice
group.insert( 2, "Choice 3", null );
The #style directive can be placed before inserting an element to a ChoiceGroup.
ChoiceGroup.set() //#style choice
group.set( 2, "Choice 3", null );
The #style directive can be placed before setting an element of a ChoiceGroup.
Form constructor //#style mainScreen
Form form = new Form( "Menu" );

// in subclasses of Form:
//#style mainScreen super( "Menu" );
Use the #style directive before a Form-constructor or before calling super() in subclass-constructors.
List constructor //#style mainScreen
List list = new List( "Menu", List.IMPLICIT );

// in subclasses of List:
//#style mainScreen
super( "Menu" , List.IMPLICIT );
// in subclasses of Form:
//#style mainScreen super( "Menu" );
Use the #style directive before a List-constructor or before calling super() in subclass-constructors.

Exclude or Include Complete Files with #condition

The #condition directive can be used to prevent the usage of complete files, when a condition is not met:

//#condition polish.usePolishGui

When in the above example the J2ME Polish GUI is not used (and thus the symbol polish.usePolishGui is not defined), the file will not be included into the application bundle for the current device. Conditions can use the same operators and comparators like the #if directive.

Excempt a Class from Obfusction with #dontobfucate

You can exclude a class from obfuscation with the #dontobfuscate directive. This is especially useful if you resort to dynamic class loading for generic devices that may or may not support specific APIs:

//#condition polish.optional-api.nokia-ui

//#dontobfuscate

package de.enough.polish.util.devicecontrol;

import com.nokia.mid.ui.DeviceControl;

public class NokiaDeviceController 
implements DeviceController, Runnable 
{
	[...]
}

Another option to exclude classes from obfuscation is to define them in the <keep> elements of the <obfuscator> configuration in your build.xml script.

Defining Temporary Symbols and Variables with #define, #defineorappend and #undefine

You can temporarily define and undefine symbols with the #define directive. This can be used to evaluate a complex if-term only once and then referring a simple symbol instead of using the complex term again:

//#if !basicInput && (polish.hasPointerEvents || polish.mouseSupported)
	//#define tmp.complexInput
//#endif

You can later just check the temporary defined symbol:

//#ifdef tmp.complexInput
	doSomethingComplex();
//#endif
Directive  Explanation
//#define [symbol] Defines the specified symbol. You can never define the symbol "false".
//#undefine [symbol] Removes the specified symbol from the pool of defined symbols. You can never undefine the symbol "true".

Symbols defined in this way are only available in the same source-file where you defined that symbol. It is advised that you start the names of defined symbols with "tmp.", so that you always know that this is a temporary symbol.

You can temporarily define and undefine variables with the #define as well. When the expression after the #define directive contains the equals-sign, the left part will be taken as the variable name, and the right part as the variable value:

//#if !message:defined
	//#define message = Hello world
//#endif

You can later just use the defined variable normally:

//#= String message = "${ message }"

When you have complex rules for implementing interfaces, you might want to use the #defineorappend directive. This allows you to add values to a single variable step by step. The following example is taken from J2ME Polish's TextField implementation that

//#if !(tmp.forceDirectInput || polish.blackberry || polish.doja) || tmp.supportsSymbolEntry
	//#defineorappend tmp.implements=CommandListener
	//#define tmp.implementsCommandListener
//#endif
//#if polish.TextField.suppressCommands == true
	//#define tmp.suppressCommands
//#else
 	//#defineorappend tmp.implements=ItemCommandListener
	//#define tmp.implementsItemCommandListener
//#endif
//#if polish.blackberry
	//#defineorappend tmp.implements=FieldChangeListener
//#endif
//#if false
	//#define tmp.implementsCommandListener
	//#define tmp.implementsItemCommandListener
	implements CommandListener, ItemCommandListener, FieldChangeListener
//#else
	//#= implements ${tmp.implements}
//#endif
Directive  Explanation
//#define [name]=[value] Defines the specified variable with the given value.
//#defineorapend [name]=[value] Either defines the specified variable with the given value or adds the given value with a comma to the existing value.
//#undefine [name] Removes the specified variable.

Inserting Code Fragments with #include

You can insert complete code fragments with the #include directive:

//#include ${polish.source}/includes/myinclude.java

Within the file-name you can use all defined variables. A useful variable is especially "polish.source", which points the base directory of the file, which is currently preprocessed. I you use only relative names, please bear in mind, that the base directory is the root of your project (or to be more precise: the directory which contains the build.xml file).

Analyzing the Preprocessing-Phase with #message and #todo

Sometimes the preprocessing itself is rather complex. The #message directive can help to understand the process by printing out messages during the build-process:

//#if !basicInput && (polish.hasPointerEvents || polish.mouseSupported)
	//#define tmp.complexInput
	//#message complex input is enabled
//#else
	//#message complex input is disabled
//#endif

Within each message any variables can be used. If a variable is not defined, the variable-definition will be included in the message.

//#message using the update-url ${update-url}.

The above example results in the message "MESSAGE: using the update-url http://update.company.com." being given out, when the variable "update-url" has the value "http://update.company.com". When this variable is not defined, the message "MESSAGE: using the update-url ${update-url}." will be printed out instead.

The #todo directive works very similar like the #message directive, but it adds the name of the source-code and the line in which the #todo-directive is embedded before the actual message (e.g. "TODO: MyMidlet line 12: Implement pauseApp()").

Handling Several Values in Variables with #foreach

The #foreach directive can be used for processing a variable which has several values separated by comma. An example is the "polish.SoundFormat"-variable which lists all supported audio-formats of a device, e.g. "midi, amr, mp3".

The #foreach directive defines a temporary loop-variable and ends with the "#next [loop-var-name]" directive:

//#foreach [loop-var-name] in [variable-name]
...[any directives and code]...
//#next [loop-var-name]

The following example illustrates this:

String format;
//#foreach format in polish.SoundFormat
	format = "${ lowercase( format ) }";
	System.out.println( "The audio-format " + format + " is supported by this device." );
//#next format

You can use any number and any kind of code and preprocessing directives within the #foreach-loop itself. So you can even use nested #foreach loops, for example. The code-fragment within the #foreach-loop will be copied into the preprocessed source-code as many time as there are values. In the above example it would be copied three times, since in the example three formats are supported. This copying process has two implications:

  1. Any variables should be defined outside of the loop (but they can be set and used within the loop, of course), and
  2. When the application is debugged or compiled, breakpoints or errors can point to the wrong source-code lines.

If you keep these implications in mind you have another powerful directive at your hand.

back to top