StrataCode Framework Development

StrataCode lets you build powerful frameworks - those that can transform large, complex Java projects into something more.

StrataCode can grok an entire large Java project from source code, or a mix of source and Java binaries (jar/class). It provides a complete type-aware API for navigating types, dependencies, attributes, etc. It exposes just the right set of hook points for letting you add annotation handlers, or process types which extend other types. You can mix code into the class definitions, convert fields to get/set methods, add wrapper methods that inject events into a setX method, support data binding, enable component features, or write custom code processors that run on selected types. There are APIs which let a framework developer make natural API calls to make incremental changes to a Java language model which are then incrementally applied to the file.

To get started with Framework Development using StrataCode, first read about the Build and Packaging Runtime

If you just want to play around with the APIs, you can start with the simpleParser example.

Framework layers are just regular layers that tend to do more. For example, it may register for one or more annotation handlers. When a specific annotation is used on a class, you can mixin a code-template to that class, or write code to do some type of special processing. You can accumulate all instances of a given class into a 'type group' - which can then be enumerated in some data structure elsewhere in the system (for example, generating an XML file). You can also define and configure 'object templates' and 'new templates'. For a given base class, you can specify a code-template used to augment the class. If it's a class and a component, this template generates a 'newX' method. If it's an object, it generates the getX method used to retrieve the object instance.

Sometimes a framework layer needs to wrap a framework to inject code so we can use that class with data binding. In other cases, you can add this support via an annotation layer. An annotation layer can modify classes for which we do not have the source, but in a restricted way. Certain annotations can be set on compiled types in the annotation layer which do not alter the type's source code, but instead change how StrataCode processes any references to that type. For example, you can suppress binding warnings, add some mix-in code to each sub-class of the base class, mark a property as read-only and specify templates to be used for all object, or component references to this base-class.

See @CompilerSettings, @Constant, @Bindable, @BindSettings for details on the StrataCode annotations).

The swing integration provides examples of both annotation layers and the wrapper layer. Annotation layers are called "meta" by convention.

Annotation Layers

An annotation layer allows you to add annotations to pre-compiled Java classes without generating a new type or wrapper class in the system. The annotations affect how the pre-compiled class is used.

For example, the AWT annotation layer is quite simple. It annotates the classes java.awt.Point and java.awt.Dimension. These are objects which hold the x, y, width and height properties in awt. But since they do not send PropertyChange events, StrataCode can't monitor their changes. Instead, the swing system adds binding events for the location and size parent objects. Ordinarily StrataCode warns you about binding expressions to properties which do not fire events. To avoid these, the awt/meta layer marks these properties constant from StrataCode's perspective with the @Constant annotation. StrataCode will not let you set their values and won't give you a warning about the lack of a binding event when you use them in a binding expression.

Here's the awt.meta layer definition:

package java.awt;

awt.meta {
   annotationLayer = true;

and the files which annotate the Point and Dimension classes:

file: awt/meta/
Point {
   override @Constant x;
   override @Constant y;

file: awt/meta/
Dimension {
   override @Constant width;
   override @Constant height;

The swing layer has its own annotation layer:

file: swing/meta/
package javax.swing;

public swing.meta extends awt.meta { codeType = sc.layer.CodeType.Framework; codeFunction = sc.layer.CodeFunction.UI;

annotationLayer = true;

compiledOnly = true; // This layer does not run in dynamic mode }

An annotation layer can also set the @Component annotation on any class. This permits you to use the component initialization semantics without adding a wrapper layer of classes. Swing classes like JMenu, which do not require a wrapper class for data binding events, are defined in the annotation layer to avoid creating a wrapper class. In this example, anyone using a regular JMenu class will initialize it with component semantics using the specified code templates.

file: swing/meta/
JMenu {


The Swing Core Layer

The Swing core layer injects data binding into Swing using simple wrapper classes for most components. A few properties are added and event listeners fire change events.

Here's the layer definition file:

file: swing/core/
package sc.swing;

import java.awt.Point;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Container;
import java.awt.Font;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Insets;
import java.awt.Cursor;

import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseListener;

import java.util.Enumeration;

import javax.swing.text.StyledDocument;
import javax.swing.ImageIcon;
import javax.swing.Icon;
import javax.swing.BorderFactory;

import javax.swing.UIManager;
import javax.swing.SwingUtilities;

import javax.swing.JRadioButtonMenuItem;
import javax.swing.JMenu;
import javax.swing.JMenuBar;

import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JDialog;
import javax.swing.JPopupMenu;
import javax.swing.AbstractButton;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.ListSelectionModel;
import javax.swing.JTabbedPane;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;

import sc.swing.*;

swing.core extends swing.meta, util {
   codeType = sc.layer.CodeType.Framework;
   codeFunction = sc.layer.CodeFunction.UI;

   compiledOnly = true; // No real reason to make this layer dynamic ever and something about seems to make it not work

   void init() {

   public void start() {
      sc.layer.LayeredSystem system = getLayeredSystem();
      sc.layer.LayerFileProcessor resourceFileProcessor = new sc.layer.LayerFileProcessor();

      resourceFileProcessor.prependLayerPackage = true;
      resourceFileProcessor.useSrcDir = false;
      resourceFileProcessor.useClassesDir = true;

      registerFileProcessor(resourceFileProcessor, "png");
      registerFileProcessor(resourceFileProcessor, "gif");
      registerFileProcessor(resourceFileProcessor, "jpg");

      sc.lang.DefaultAnnotationProcessor mainInitProc = new sc.lang.DefaultAnnotationProcessor();
      mainInitProc.typeGroupName = "mainInit";
      mainInitProc.validOnField = false;
      mainInitProc.validOnObject = true;
      mainInitProc.createOnStartup = true;

      registerAnnotationProcessor("sc.swing.MainInit", mainInitProc);

      addTypeGroupDependency("", "sc.swing.Main", "mainInit");


Processing is added for resource files with the png and gif suffix. These files are copied into the build dir unless overridden replaced in a subsequent layer.

The MainInit annotation is defined. All classes tagged with @MainInit are put into a type group called mainInit. This type group is evaluated in the code template for the class. For incremental builds, a dependency is added so is regenerated if elements are added or removed from the type group.

These imports are exposed both to classes in the layer and for users of the layer without an explicit import. A subsequent layer could choose to not inherit imports at all with inheritImports=false. If you want to use imports internally only, not expose them to extended layers, set exportImports=false.

The wrapper class for a primitive component sets the @Component attribute, and triggers a change event for the "preferredSize" property when the text changes:

file: swing/core/
import sc.bind.*;
import sc.type.IBeanMapper;
import sc.type.TypeUtil;

public class JLabel extends javax.swing.JLabel implements ComponentStyle {
   public static IBeanMapper textProp = TypeUtil.getPropertyMapping(JLabel.class, "text");

   @Bindable(manual = true)
   public void setText(String text) {
      Bind.sendEvent(IListener.VALUE_CHANGED, this, SwingUtil.preferredSizeProp);
      Bind.sendEvent(IListener.VALUE_CHANGED, this, textProp);

   public void setIcon(Icon icon) {
      Bind.sendEvent(IListener.VALUE_CHANGED, this, SwingUtil.preferredSizeProp);

   override @Bindable(manual=true) preferredSize;
   override @Bindable size;
   override @Bindable location;
   override @Bindable visible;
   override @Bindable foreground;
   override @Bindable background;

The JPanel class associates template objects which add sub-objects as children. By default, data binding is used so the default layout is also set:

file: swing/core/
@CompilerSettings(objectTemplate="javax.swing.JComponentInit", newTemplate="sc.swing.JComponentNew")
public class JPanel extends javax.swing.JPanel {
   layout = null;

The JComponentInit template set via CompilerSettings is used to define a code snippet inserted into the declaring class when an object tag is used of that type. It is passed an object which contains all of the properties you need to evaluate. Here's the template for the swing component:

file: swing/meta/JComponentInit.sctd
 * Snippet to be inserted for each object definition which extends the swing JComponent class
 * Accumulates the children objects and adds them.
<% if (!overrideField && !overrideGet) { %>
   <%=fieldModifiers%> <%=variableTypeName%> <%=lowerClassName%>;
<% } %>
<%=getModifiers%> <%=variableTypeName%> get<%=upperClassName%>(boolean doInit) {
<% if (overrideGet) { %>
   <%=variableTypeName%> <%=lowerClassName%> = (<%=variableTypeName%>) super.get<%=upperClassName%>();
<% } %>
   if (<%=lowerClassName%> == null) {
      <%=lowerClassName%> = <% if (typeIsCompiledComponent) { %><%=typeClassName%>.new<%=typeBaseName%>(false)<% } 
                               else { %>new <%=typeName%>()<% } %>;
     <% if (overrideGet) { %>
     <% } %>
     <%=getDynamicTypeDefinition(lowerClassName, 2)%><%=propertyAssignments%>
     java.util.List _children = (java.util.List) <%=lowerClassName%>.getClientProperty("sc.children");
     if (_children == null)
         _children = java.util.Arrays.asList(<%=childrenNames%>);
     for (Object _child:_children) {
        sc.swing.SwingUtil.addChild(<%=lowerClassName%>, _child);
     if (doInit) {
     return <%=returnCast%><%=lowerClassName%>;
  else return <%=returnCast%><%=lowerClassName%>;
<%=getModifiers%> <%=variableTypeName%> get<%=upperClassName%>() { return get<%=upperClassName%>(true); }

See for the object passed to the init or new templates.

The JComponentNew.sctd file is used when you mark a class with @Component. It generates a newX method for each constructor and rewrites all uses of the constructor for this class to use newX instead.

The rest of the swing components are mostly thin wrappers adding binding properties where necessary. In one case, it calls invalidate/validate after a size property has changed as swing failed to detect that itself.

To integrate data binding with the Swing event thread, the listeners will use sendDelayedEvent, and processStatement to put the event delivery and script commands back on the swing thread.

Android View Example

Android View components do not have a zero argument constructor. Instead they must be created with a Context parameter. To support this pattern with the "object" operator, you can use the enclosing root level Activity object as the context for any children created inside of it.

First, associate the View class with the ViewObj code template:

file: android/meta/view/
View {}

The propagateConstructor parameter specifies that the constructor which takes the Context parameter is propagated to any subclasses unless those subclasses define that constructor explicitly. This eliminates the need to have to manually copy a trivial constructor into each class.

Then specify a code template which uses the rootName variable as the first parameter to the constructor:

file: android/meta/view/ViewObj.sctd
 * Snippet to be inserted for each object definition which extends the android View class
 * Must be inside of an Activity component.
<% if (rootName == null) 
      throw new IllegalArgumentException("Objects of type: " + typeName + 
                                         " must be children of an Activity/Service"); %>
<% if (!overrideField && !overrideGet) { %>
   <%=fieldModifiers%> <%=variableTypeName%> <%=lowerClassName%>;
<% } %>
<%=getModifiers%> <%=variableTypeName%> get<%=upperClassName%>(boolean doInit) {
<% if (overrideGet) { %>
   <%=variableTypeName%> <%=lowerClassName%> = super.get<%=upperClassName%>();\
<% } %>
   if (<%=lowerClassName%> == null) {
   <%=lowerClassName%> = <% 
      if (typeIsComponentClass) { 
      } else { 
          %>new <%=typeName%>(<%=rootName%>)<% 
      } %>;
<% if (overrideGet) { %>
<% } %>
     if (doInit) {
     return <%=lowerClassName%>;
  else return <%=lowerClassName%>;
<%=getModifiers%> <%=variableTypeName%> get<%=upperClassName%>() { 
   return get<%=upperClassName%>(true); 

Main Settings

One of the sad features of Java is that after you compile your program, you are left with the frustrating task of trying to find the right class path and main class to run it. StrataCode helps by supporting the @MainSettings annotation, typically set on a framework class so it's transparent to the application developer. It generates a ".sh" script to run your application. It also will generate a "main.jar" file which you can use to package it up for you. The script works for either compiled or interpreted execution. You can enable debugging, set JVM parameters, or produce a jar file via MainSettings.

The swing layer's main provides an example of how to use this feature:

file: swing/core/
import sc.bind.Bind;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import sc.obj.MainSettings;
 * A simple main you can use for your swing components.
 * Children of this object are automatically instantiated at startup
public object Main {
   /** Access to the args array provided to the main method (if any) */
   public static String[] programArgs;
   /** Create and register a binding manager to ensure events are delivered only on the swing thread */
   object bindingManager extends SwingBindingManager {
   object statementProcessor extends SwingStatementProcessor {

   public static void main(String[] args) {
      programArgs = args;
      //Schedule a job for the event dispatching thread:
      //creating and showing this application's GUI.
      SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               // Need to update the event handling thread with the current class loader so JPA and things can use it to find
               // resources
               ClassLoader cl = sc.dyn.DynUtil.getSysClassLoader();
               if (cl != null)

               Main main = Main; // Referencing the main object will create it and kick everything off
               // If any dynamic types are registered with the @MainInit when we start, grab them as well
               // Actually, we'll compile in any such references using dynamic new calls
               //Object[] dynInitObj = sc.dyn.DynUtil.resolveTypeGroupMembers("mainInit");

Also check out how StrataCode's own compiler's main is defined (since StrataCode builds StrataCode).

class LayeredSystem {
   public static void main(String[] args) {