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>
Jar download available here : https://repo1.maven.org/maven2/org/ainslec/picocog/1.0.7/picocog-1.0.7.jar
3. Sample Usage
3.1. Basic
The basic example simply demonstrates indentation.
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());
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.
// 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());
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 : [email protected]
Follow me : @ainslec