Java+ strings and executable inclusions are specified by
nested pairs of digraphs, called Java+ delimiters. The
default delimiters are {{ and }}
which will will be used in all examples for concreteness, but
this is configurable at run time. For example, JSP devotees can change them
to <% and %> to feel right at home.
The delimiters address these limitations of ordinary quoted Java strings:
| Java+ | Java |
|---|---|
System.out.println(
| System.out.println( |
| Java+ | Java |
|---|---|
System.out.println( |
System.out.println( |
public static void main(String[] args)
{
System.out.println(bndl.getString("greeting"));
}These are minor annoyances in these simple examples, but large programs have hundreds of strings and demand automated support as the program evolves. Java+ provides a simple string syntax that solves these problems at pre compile time, while adding absolutely no overhead in either space or time.
By default (without the localization option), Java+ simply converts Java+ strings shown on the left in all these examples into the plain Java strings shown on the right. In the simplest one-line case, Java+ and Java strings are exactly the same:
| Java+ | Java |
|---|---|
System.out.println( |
System.out.println( |
Here is a longer example with embedded quotes and multiple lines:
| Java+ | Java |
|---|---|
System.out.println( |
System.out.println( |
If part of the string is to be computed instead of hard wired, enclose any non-void java expression in digraphs like this:
| Java+ | Java |
|---|---|
System.out.println( |
System.out.println( |
Java+'s behavior is entirely determined by digraph nesting level. It knows nothing of Java except how to emit Java strings and how to exempt comments, ordinary strings, and character constants from digraph processing. The opening digraph begins a multi line string and the closing digraph ends it. The same digraph pair delimits executable inclusions within Java+ strings.
The executable inclusion feature relies on the fact that Java will automatically convert any type concatenated with a string by calling the types toString() method. All objects inherit this from Object, so executable inclusions can be of any type except void.
Java+ always emits exactly one line of output per input so line numers reported in compiler or runtime diagnostics will match the Java+ input.
For documentation such as this page, which must express java+ delimiters as ordinary non-functional text, insert a backslash between the first and second characters of each digraph to escape the usual processing.
When the bundle option is set, Java+ redirects all Java+ strings
to a ResourceBundle file and replaces them with the appropriate
bndl.getString() calls. This is completely automatic. Just select
the bundle option and provide a variable name that is not used in
your program. Java+ automatically add a final static variable after
your first class declaration that defines a variable by that name,
initialized with a reference to the program's ResourceBundle. The only intervention required is to configure Java+ so that it emits its .properties files into the directory that holds your .class files. The statement Java+ adds is marked with an Added by Java+ comment as shown below:
package edu.virtualschool.javaplus;
public class Sampler
{
/* Added by Java+ */
private final static java.util.ResourceBundle bndl=
java.util.ResourceBundle.getBundle("edu.virtualschool.javaplus.Sampler",
java.util.Locale.getDefault());
...
}
This example is artificially formatted. The additional text is actually inserted without newlines so that line numbering will be the same in the input and output files. Java+ then converts input on the left into the output on the right.
| Java+ | Java |
|---|---|
public static void
main(String[] args)
{
System.out.println(
|
public static void
main(String[] args)
{
System.out.println(bndl.getString(
|
The properties file for Sampler.class (Sampler.properties) looks like this:
#ResourceBundle for /Volumes/a4/bcox/Projects/java+/./Sampler.java #Fri Jan 03 21:12:28 EST 2003 1132841599=\nThe answer, \nmy dearest, \nis 1436=.\n -1199734339=my computed answer
Java+ strings are retained as comments in the Java output for
readability and to keep line numbers in sync. If a Java+ string contains
a */ comment closure it is converted to *-/.
ResourceBundle keys (getString arguments) are invarient so long as the string contents are unchanged. They are computed as the string's hash code, with any collisions resolved by appending a period plus digits for uniqueness. This helps to keep translated ResourceBundle files in sync with the master as the code evolves over time.
Arbitrary-length strings with executable inclusions are generally useful for error messages, SQL statements, and so forth, but truly indispensible in these cases:
The following example shows the idea. The Meta structure at the top provides the information that will appear in the navigation bar:
package html;
import edu.virtualschool.jwaa.Fault;
import edu.virtualschool.jwaa.StaticPage;
import edu.virtualschool.jwaa.MetaPage;
public class SyntaxPage extends StaticPage
{
public final static MetaPage meta = new MetaPage(
SyntaxPage.class,
"/java+/syntax.html",
"Syntax",
"Java+ String Syntax",
null,
new MetaPage[] { }
);
public SyntaxPage() { super(meta); }
public final void run() throws Fault
{
JavaPlusFormat.sendPrefix(this);
send({{
[The html text for this page]
[A link to {{link(AnotherPage.meta)}} is done like this]
}});
JavaPlusFormat.sendPostfix(this);
}
}
ResourceBundle errors can be hard to diagnose because they occur at class initialization time and because they involve the class loader which is not only hard to understand, it only reports "I couldn't find it" without bothering to tell you where it was looking. More to come as I understand class loader errors better myself.
See Make, Ant or GUI in the navigation bar for detailed usage instructions.
| Java+ Version 2.0 | © Copyright 2003 by Brad Cox | 12 Dec 2003 |