Java+ Syntax

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:

Multi line strings
Java's string syntax is fine for short, single line strings but quite unwieldy for multi line strings such as elaborate printouts, error messages, xml, html and so forth.
Java+Java
System.out.println({{
 <html>
  <body>
   ...and so forth
  </body>
</html>}});
System.out.println("\n"+
"<html>\n"+
"  <body>\n"+
"   ...and so forth\n"+
"  </body>\n"+
"</html>");
In all examples, black text will highlight java code and red text will highlight strings. Java+ input will be shown on the left and the generated Java source code on the right for comparison.
Executable inclusions
Java code can be nested within Java+ strings by enclosing any non-void Java expression in Java+ delimiters like this:
Java+Java
System.out.println({{
<xmlExample>
  <name>{{myName}}</name>
  <number>{{myNumber}}</number>
</xmlExample>
}});
System.out.println("\n"+
"<xmlExample>\n"+
"  <name>"+myName+"</name>\n"+
"  <number>"+myNumber+"</number>\n"+
"</xmlExample>");
The nesting can continue to any depth, although this isn't common in paractice. Java+ simply passes black java code through unchanged (even numbered levels, including 0th) while translating red strings (odd numbered nestings) to quoted string syntax.
Internationalized Strings (Localization)
Applications support multiple natural languages by segregating all user-visible strings users into properties files. A translation services is then retained to translate the native language properties file to each of the languages to be supported. At runtime, Java's ResourceBundle class loads the right file according to the user's Locale.
This complicates development because strings are no longer in the source code but in separate files that have to be kept in sync as the program evolves. For exaple, the only clue that this is the familiar "Hello World" program is the "greetings" tag in the getString() statement.
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.

Multi line Strings with Executable Inclusions

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({{Hello world}});
System.out.println("Hello world");}

Here is a longer example with embedded quotes and multiple lines:

Java+ Java
System.out.println({{
The answer,
my dearest, 
is "yes".
  }});
System.out.println("\n"+
"The answer\n"+
"my dearest,\n"+
"is "yes".\n"+
"  ");

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({{
The answer,
my dearest, 
is {{computeAnswer()}}.
  }});
System.out.println("\n"+
"The answer\n"+
"my dearest,\n"+
"is "+computeAnswer()+".\n"+
"  ");

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.

Localization

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({{
The answer,
my dearest, 
is {{computeAnswer()}}.
  }});
}
static String computeAnswer()
{
  return {{my computed answer}};
}
public static void 
main(String[] args)
{
  System.out.println(bndl.getString("1132841599")/*
The answer, 
my dearest, 
is */+computeAnswer()+bndl.getString("1436")/*.
*/);
}
private static String computeAnswer()
{
  return bndl.getString("-1199734339")/*my computed answer*/;
}

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.

Common Usage Examples

Arbitrary-length strings with executable inclusions are generally useful for error messages, SQL statements, and so forth, but truly indispensible in these cases:

Static Html Generation
Writing web pages by hand in html is no problem for single pages, but excruciating when multiple pages must hotlink to each other and exhibit a common look and feel. It is far easier to generate them with code in which content of each page is a long Java+ string. For example, each page in this documentation was generated by a Java+ class in the src/html directory. The body text is a single long string, and the executable inclusion feature is used to emit hotlinks to other pages. The look-and-feel is provided by an shared class, JavaPlusFormat, which provides static methods that the other classes call to emit the boilerplate at the beginning and end of each page. The boilerplate code emits a navigation bar at the top and bottom of each page based on a data structure defined in each class.
Dynamic JSP/ASP Replacement
Java+ was originally developed as a JSP/ASP alternative. It is substantially (orders of magnitude) faster, smaller, and more secure because it eliminates the need for compilers on deployment servers (a security concern). The technique is very similar to the example described above, except each page has a different superclass to adapt it to being generated on-the-fly. The Java Web Application Architecture distribution (JWAA) provides all support classes. By making web pages first-class Java objects that reference each other as any other Java object, invalid hotlinks will be detected at compile time, so dead links are never seen by the user.

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);
  }
}

Common Errors

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.

I changed the bundle option and nothing happened
Use the force option when making global changes of this sort. Java+ skips inputs when output files are up to date.
My edits got lost
Only make changes to Java+ input files. This error is common when using an IDE, but with plain old ctags and text editors it is easy to avoid: just include the java+ source directory when building the index.
java.util.MissingResourceException: Can't find bundle for base name
ResourceBundle can't find the bundle file for your class, probably because you told Java+ to store your .property files somewhere the class loader isn't looking for them. They must be in the same package subdirectory with your .class files and copied from there into your jar.
Use the jar tool to examine your .jar's contents (jar tv jarfile.jar). Look for the .class AND the .properties file of each of your top-level classes (inner classes don't matter) to be in each of your directories.
java.util.MissingResourceException: Can't find bundle for key n
ResourceBundle found the bundle file but no value in it matched key "n", where "n" is one of the keys in your program. Your bundle file (.properties) is out of date with respect to the class for reasons explained above.

Bugs and Limitations

Usage Instructions

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
Served by John Companies, Inc.