Skip to content

ainslec/picocog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Picocog

1. Intro

Picocog is a tiny library for use in formatting text programatically (indents and placeholders) for the purposes of source code generation.

Picocog’s only purpose is to output indented text, don’t expect anything fancier than that. Picocog supports deferred writing, indentation and not much more.

The initial release is just 6K. Size doesn’t matter.

2. Maven dependency

(see GWT section for additional GWT dependency)

   <dependency>
      <groupId>org.ainslec</groupId>
      <artifactId>picocog</artifactId>
      <version>1.0.7</version>
   </dependency>

3. Sample Usage

3.1. Basic

The basic example simply demonstrates indentation.

Generator Source
PicoWriter w = new PicoWriter();

w.writeln("int foo = calcFoo();");
w.writeln("");
w.writeln("// We shall dance here");
w.writeln_r("if (foo == 0) {");
w.writeln("sayHello();");
w.writeln_lr("} else if (foo < 100) {");
w.writeln("sayGoodbye();");
w.writeln_lr("} else {");
w.writeln("sayAnything();");
w.writeln_l("}");

System.out.println(w.toString());
Generated Source
int foo = calcFoo();

// We shall dance here
if (foo == 0) {
   sayHello();
} else if (foo < 100) {
   sayGoodbye();
} else {
   sayAnything();
}
Note
Adding in comments, even silly ones, is extremely important when generating sourcecode. They act as markers so that it is easy to locate the corresponding code generator code.

3.2. Advanced

This example demonstrates out of sequence writing (deferrals).

A deferral can be placed within the write stream, and then can be used to insert code at the reservation point. The point at which the call to createDeferredWriter() is called is significant. Indentation levels are inherited.

This is useful where there are blocks of code in different parts of the source that need to be updated at the same time.

Although not demonstrated here, it is possible to create deferrals within deferrals using the same API.

Generator Source
  // This is our top level source formatter
  PicoWriter topWriter = new PicoWriter();

  String myPackageName = "com.samplepackage";
  String myClassName   = "MyClass";

  topWriter.writeln ("package " + myPackageName + ";");
  topWriter.writeln ("");
  topWriter.writeln_r ("public class "+myClassName+" {");

  PicoWriter memvarWriter    = topWriter.createDeferredWriter();
  topWriter.writeln_r ("{");
  PicoWriter indentedSection = topWriter.createDeferredWriter();
  topWriter.writeln_l ("}");
  topWriter.writeln("");

  // Reserve a place at the current row
  PicoWriter methodSection = topWriter.createDeferredWriter();

  memvarWriter.writeln("String myString = null;" );
  indentedSection.writeln("// Contents of the indented section (1)");
  memvarWriter.writeln("String myString2 = null;" );
  indentedSection.writeln("// Contents of the indented section (2)");

  // Reserve a place at the current row
  PicoWriter mainMethod = methodSection.createDeferredWriter();

  mainMethod.writeln_r("public static void main(String[] args) {");
  mainMethod.writeln_r("if (args.length == 0) {");
  mainMethod.writeln("System.out.println(\"Require more than one argument\");");
  mainMethod.writeln_lr("} else if (args.length == 1) {");
  mainMethod.writeln("doSomething();");
  mainMethod.writeln_lr("} else {");
  mainMethod.writeln("System.out.println(\"Too many arguments\");");
  mainMethod.writeln_l("}");
  mainMethod.writeln_l("}");
  mainMethod.writeln("");
  topWriter.writeln_l ("}");

  // To extract the source code, call .toString()
  System.out.println(topWriter.toString());
Generated Source
package com.samplepackage;

public class MyClass {
   String myString = null;
   String myString2 = null;
   {
      // Contents of the indented section (1)
      // Contents of the indented section (2)
   }

   public static void main(String[] args) {
      if (args.length == 0) {
         System.out.println("Require more than one argument");
      } else if (args.length == 1) {
         doSomething();
      } else {
         System.out.println("Too many arguments");
      }
   }

}

4. FAQ

4.1. Why not use a templating library?

It’s highly subjective but generating sourcecode using Java code is much easier to debug imho. There is almost no learning curve, and anyone can debug.

Templates can be easier to read, but they can also be very complex, and involve reflection, something which makes debugging difficult.

With Java based code generation, you control everything, and you understand everything.

A Template (Velocity) is generally better for shallow complexity, programatic code generation (Picocog, Java Poet) is better for deep complexity.

4.2. How do I escape JSON/XML/YAML/Java text?

It’s a slippery slope, so Picoclogs ships with no text escaping utility methods.

If you want to escape common text formats, the following library is useful:

4.3. Alternatives

Some alternative technologies.

Warning
Some of these products are over 16 Kilobytes in size.

4.3.1. Java Code Based

4.3.2. Template Based

4.3.3. Articles

4.4. JSR 269

Picocog can come in useful for iterating over annotations on source code, and from those annotations, generating new source code.

Of course, you can do this without Picocog too, but Picocog is tailor-made for this kind of use-case.

See :

4.5. GWT

Picocog is compatible with client-side GWT.

To use Picocog in your client side code:

1a - For non maven projects, make sure picocog source code is on your classpath.
1b - For maven projects, add the following dependency to your GWT Client POM (GWT requires source code + module gwt.xml):

   <dependency>
      <groupId>org.ainslec</groupId>
      <artifactId>picocog</artifactId>
      <version>1.0.7</version>
      <classifier>sources</classifier>
   </dependency>

2 - Add the following line to your client project .gwt.xml file:

<inherits name='org.ainslec.picocog.Picocog'/>

5. Tips / Tricks

  • It helps to create a bunch of deferrals straight away.

  • Always match indents at point of writing. Never wait to match an indent.

  • Always generate distinctive comments in your generated source - these act as anchors for debugging your source code generator.

  • Always use the writeln_lr for '} else if (…​) {' lines.

  • Be aware of the escaping requirements of the language for which you are generating sourcecode.

  • Store commonly use strings in member variables and/or constants.

  • If you have access to the API at code generation time for which you are generating source for, make use of the YourApiClass.class.getName() method call. This will make sure that your code generator will automatically cope with class name refactoring.

  • If you are nice to other people, they tend to act nicer towards you.

6. Contact

Email : c.b.ainsley@gmail.com
Follow me : @ainslec

About

A tiny code generation library (< 8 KB) written in Java, useful for any purpose, but ideal for JSR-269

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages