Command line example
The command line interface supports a very flexible, and powerful way to build, control, and edit StrataCode programs line-by-line. It works in conjunction with the program editor if you run them both against the same EditorContext. As you navigate to a type, the command line does as well and vice-versa. It works in two modes - 'edit mode' and 'script mode'. In edit mode, you navigate to a type (or really a specific layer of a type) and expressions, property assignments, fields, and methods you type in are added or modify those that exist in the current layer of that type. In script mode, new fields, methods etc. work just like edit mode. But for property assignments, they will only apply to the current instance. In edit mode, when there are current instances of that type, those changes will be made to the instances if possible. If not, a warning is given and you can use cmd.restart() to recompile and re-run the application (this is not super reliable yet but we can build reliable frameworks that support live programming for key management UI use cases, or automate the restart when major changes are made to an application).

Build a swing app from the command line

This example shows how to create a simple swing unitConverter application using the command line editor.

Create a layer

To learn StrataCode start by writing a unit converter application which converts numbers from one unit to another, e.g. celcius to fahrenheit. Change to your /home/StrataCode directory and run scc with no arguments:
[/home/StrataCode]% scc

No current layers.
    
Create a new layer?: [y] 
Hit enter to create a new layer. StrataCode responds:
Enter new layer directory: /home/StrataCode/layers/
Enter a layer name to store your layer. To create a family of layers, use a directory or dotted suffix:
Enter new layer directory: /home/StrataCode/layers/unitDemo/model
 
Types dynamic by default? y/n: [y] 
Hit enter to choose the default. At least initially all types in this layer will be dynamic. Dynamic types can be modified more freely than compiled types at runtime and are great for prototyping.
Members public by default? y/n: [y] 
Hit enter again. Making all members public by default simplifies the code.
Package: [] 
Specify a package prefix to prepend onto all types in this layer. Both .java and .sc files do not have to specify a package. They also do not have extra directories Java usually requires for that package. Many layers are single directories containing just a few files.
Package: [] unitDemo

Layers to extend (optional): [] 
Here you can list layers by path name or type name which your layer includes or modifies. Use TAB to see valid choices. Your layer can only modify types inside of its package. For this sample, you'll include the util layer.
Layers to extend (optional): [] util

About to create layer definition file: /home/StrataCode/layers/unitDemo/model/model.sc:

dynamic public unitDemo.model extends util {
}

Create? [y]: 
The wizard creates the layer definition file "model.sc" and puts it in the "model" directory inside of the unitDemo group of layers. Any imports in this file are visible to code in the layer and, by default, layers which extend this layer. A framework layer imports commonly used classes to create a convenient sandbox for code using that layer. Once you hit enter, StrataCode responds:

Adding layer: unitDemo.model extends: [util(sc.util)]
Compiling util(sc.util) into: /home/StrataCode/layers/util/build using:
No files to compile
Compiling into: /home/StrataCode/layers/unitDemo/model/build layers: [util(sc.util)] dynamic: [unitDemo.model(unitDemo)]
No files to compile
(unitDemo.model:unitDemo) -> 
(Note: if your util layer was not built separately, i.e. you have not run "scc -c util", it will compile files from util and unitDemo.model at this time).

Defining the Domain Model

Start by defining the domain model for the unit converter. You'll need two floating point numbers to store the values to convert between, titles for each unit, and a title for the conversion itself. The class Converter will be used to store each type of converter. Adding a new conversion will require configuration of a new instance of this class.

Place the code below into the file unitDemo/model/UnitConverter.sc, or just paste this text directly into the command line. In this case, StrataCode creates unitDemo/model/UnitConverter.sc automatically.

file: unitDemo/model/UnitConverter.sc
object UnitConverter {
   static class Converter {
      double value1, value2;
      String unit1, unit2;
      String title;
      public String toString() {
         return title;
      }
   }
   
   object converters extends ComponentList<converter> {
      object temperature extends Converter {
         value1 = 0;
         value2 :=: value1 * 9.0 / 5.0 + 32; 
         unit1 = "Celcius";
         unit2 = "Farenheit";
         title = "Temperature";
      }
      object distance extends Converter {
         value1 = 0;
         value2 :=: value1 * 0.62137119;
         unit1 = "Kilometers";
         unit2 = "Miles";
         title = "Distance";
      }
      object speed extends Converter {
         title = "Speed";
         value1 = 0;
         value2 :=: value1 * 1.609344;
         unit1 = "Miles per hour";
         unit2 = "Kilometers per hour";
      }
   }
}
StrataCode does not yet listen for file system changes automatically so use:
cmd.refresh(); 
after you change sc files.

In addition to the Converter domain model class, there are instances of that class for temperature, distance and speed. Notice the use of the object operator where you'd normally expect to see a class. The object tag lets you define a type and an instance in one definition. You reference objects using the same naming rules as classes (imports, inner objects, etc.) but you can use object references in both situations where Java expects types and values.

Let's look at the definition of the converters object in more detail. It extends ComponentList from the util layer with elements of the newly defined type Converter. Inner objects are added to the List.

Note: In the sample, notice that you can initialize variables in either a class or an instance with a top-level variable=value statement. For example, with the two files:

file: Bar.sc:
class Bar {
   int baz;
}
file: Foo.sc:
class Foo extends Bar {
   baz = 3;
}
In Java, you have to surround these statements with { baz=3; } but StrataCode allows property assignments at the top-level. Arbitrary statements and expressions are disallowed at this level but you can put in an "a.b" expression into the left-hand side. So you can say: a.b = 3 if there is an inner type "a" with a property "b". Let's also look at this statement in more detail:
value2 :=: value1 * 9.0 / 5.0 + 32;
Notice the use of a new assignment-like operator :=:. In StrataCode this defines a forward and reverse data binding expression. When value1 changes, the expression is applied and value2 is updated. And similarly when value2 is modified, the inverse expression is evaluated and value1 is updated. Not all expressions are invertible of course. StrataCode will give a compile time error if it cannot use a reverse binding with a given expression. In this case because there is only a single variable in an arithmetic expression, you can invert the expression so the bi-directional binding is allowed. In StrataCode, a forward-only binding is specified with := and a reverse-only binding is expressed with =:. StrataCode supports most Java expressions with forward-only bindings. For a given method, you can define an inverseMethod to use that method call in a reverse binding. Because UnitConverter is an object, you can now test this code:
(unitDemo.model:unitDemo) -> UnitConverter {
(unitDemo.model:object unitDemo.UCTest) -> converters { 
(unitDemo.model:object unitDemo.UCTest.converters) -> temperature {
(unitDemo.model:object unitDemo.UCTest.converters.temperature) -> value1;
0
(unitDemo.model:object unitDemo.UCTest.converters.temperature) -> value2;
32.0
(unitDemo.model:object unitDemo.UCTest.converters.temperature) -> value1 = 100;
(unitDemo.model:object unitDemo.UCTest.converters.temperature) -> value2;
212.0
(unitDemo.model:object unitDemo.UCTest.converters.temperature) -> }
(unitDemo.model:object unitDemo.UCTest.converters) -> }
(unitDemo.model:object unitDemo.UCTest) -> }

Extending the Model Object

Extend the unitDemo application to add more conversions algorithms, but do this in a new layer:
(unitDemo.model:unitDemo) -> cmd.createLayer();
Enter new layer directory: /home/StrataCode/layers/unitDemo/extendedModel
Types dynamic by default? y/n: [y] 
Members public by default? y/n: [y] 
Package: [unitDemo] 
Layers to extend (optional): [] unitDemo/model
Make layer transparent? y/n: [n] unitDemo/model
About to create layer definition file: /home/StrataCode/layers/unitDemo/extendedModel/extendedModel.sc:
dynamic public unitDemo.extendedModel extends unitDemo.model {
}
Create? [y]: 
The cmd object is a special variable defined by the command line interpreter. It has a variety of useful methods including createLayer, which kicks off the same create layer wizard you used before.

Now place this code in unitDemo/extendedModel/UnitConverter.sc (or just paste this into the command line again):

file: unitDemo/extendedModel/UnitConverter.sc
UnitConverter {
   converters {
      object volume extends Converter {
         value1 = 0;
         value2 :=: value1 * 0.94635295;
         unit1 = "Quarts";
         unit2 = "Liters";
         title = "Volume";
      }
      object weight extends Converter {
         value1 :=: value2 * 0.035273962;
         value2 = 0;
         unit1 = "Ounces (US)";
         unit2 = "Grams";
         title = "Weight";
      }
      object area extends Converter {
         value1 = 0;
         value2 :=: value1 * 2.5899881;
         unit1 = "Square Miles";
         unit2 = "Square Kilometers";
         title = "Area";
      }
   }
}
Here you omit the class/object keyword to modify the UnitConverter object in the base layer. If you had specified instead "object UnitConverter" here, it would replace the previous version with a new object. The modify operation works much like extends but updates the existing type instead of creating a new one.

In this case you are modifying two objects one inside the other. First you modify the UnitConverter object, then you modify the converters subobject. This layer is a simple declarative layer similar to what a business analyst would manipulate. Though it's java-like, the code expresses just the business logic.

Adding the User Interface

Now let's create a new layer to add the swing user interface.

Note: Using a new layer follows a nice design pattern to keep dependencies on framework code like swing out of the domain model. This not only lets you reuse the domain model as widely as possible, it also makes the domain model code easier to read and modify. More than any other part of your system, the domain model reflects the underlying business processes you are helping to manage. A flexible and nimble domain model promotes flexible and nimble business processes. Through layering of designs, programmers can easily expose simple but powerful declarative models which expose logic and configuration directly to the people who need them. And with layers, you do not compromise performance or lose static typing, and the option to bind at compile time by putting this information into runtime-only formats like XML.

Create the swing layer:

(unitDemo.extendedModel:unitDemo) -> cmd.createLayer();
Enter new layer directory: /home/StrataCode/layers/unitDemo/swingui
Types dynamic by default? y/n: [y] 
Members public by default? y/n: [y] 
Package: [unitDemo] 
Layers to extend (optional): [] swing/core unitDemo/extendedModel
Make layer transparent? y/n: [n] 
Now modify the UnitConverter again, just like you did in extendedModel, but this time you'll add a new object named UI of type AppFrame. This is a wrapper around the JFrame swing class which creates a top-level application window. For this first step, you'll create a number converter component from the util layer to be used later, and a combo box to select a converter. Paste this into the command line or create the file unitDemo/swingui/UnitConverter.sc with these contents:
file: unitDemo/swingui/UnitConverter.sc
UnitConverter {
   @MainInit
   object UI extends AppFrame {
      object numberConverter extends sc.util.NumberConverter {
      }

      object converterChoice extends JComboBox {
         int alignx = 20;
         items = converters;
         location := SwingUtil.point(xpad+alignx, ypad);
         size := SwingUtil.dimension(windowWidth - 2 * (xpad+alignx), preferredSize.height);
      }
StrataCode's swing/core layer is a thin wrapper on the swing component set to add data binding. Class names are the same and other than adding sc data binding events, the behavior of these classes will be the same as with Swing. Because Swing does not have simple row/column layout, this sample works around that limitation by using data binding for location and size properties. Fixable with custom layout managers but for now this shows off the power of StrataCode's data binding. The SwingUtil.point and dimension methods do necessary casts and wrapping.

Properties like location and size in swing are implemented with getX/setX methods which take Point and Dimension instances. StrataCode handles that for you as well as generating getX/setX methods as needed to make properties bindable.

At this point you should see a window. Objects in StrataCode are by default lazily instantiated. The @MainInit annotation is added by the swing layer and causes the component to be initialized as part of the default swing main method. While you are still inside of UnitConverter.UI, define a currentConverter property using a forward binding:

Converter currentConverter := converters.get(converterChoice.selectedIndex);
Since converters is a list, you bind to the List.get method based on the currently selected item in the combo box. As the combo box selection changes, currentConverter's value is automatically updated to point to the selected instance: temperature, area, etc. Now the rest of the class which defines the two labels and text fields, bound to the currentConverter's values:
int row1y := (int)(ypad + converterChoice.location.y + converterChoice.size.height);

object unit1Label extends JLabel {
   text := currentConverter.unit1;  // Display's converter's "unit 1"
   labelFor = unit1Field;
   location := SwingUtil.point(xpad, row1y + baseline);
   size := preferredSize;
}
object unit1Field extends JTextField {
   location := SwingUtil.point(xpad + unit1Label.preferredSize.width + gap, row1y);
   size := SwingUtil.dimension(windowWidth - unit1Label.size.width - xpad - 2*gap, preferredSize.height);
   // Bind's text property to current converter's value1 after converter to/from string
   text :=: numberConverter.numberToString(currentConverter.value1);
}

int row2y := (int)(unit1Field.location.y + unit1Field.size.height + ypad);

object unit2Label extends JLabel {
   text := currentConverter.unit2;
   labelFor = unit2Field;
   size := preferredSize;
   location := SwingUtil.point(unit1Label.location.x, row2y + baseline);
}
object unit2Field extends JTextField {
   location := SwingUtil.point(xpad + unit2Label.preferredSize.width + gap, row2y);
   size := SwingUtil.dimension(windowWidth - unit2Label.size.width - xpad - 2*gap, preferredSize.height);
   text :=: numberConverter.numberToString(currentConverter.value2);
}

object errorLabel extends JLabel {
   // The number converter provides an error when an invalid number is supplied
   text := numberConverter.error;
   location := SwingUtil.point(xpad, unit2Field.location.y + unit2Field.size.height + ypad + baseline);
   size := preferredSize;
}
As each widget is defined, it is immediately added to the display because your swing layer is dynamic. You now have a working swing version of the UnitConverter. Notice that components robustly respond to resize events because of the data binding expressions. You can interactively adjust the xpad, ypad, baseline, and color variables to adjust the display. Do this in a new layer and you've just created a layer which applies a specific style, something suitable to hand off to a designer. Before you exit, make sure to close the UI and UnitConverter objects so UnitConverter gets saved with your changes:
    }
}

Adding a Style Layer

Now that our app is built, we want to create a layer for designers that gives them control over select properties they can use to customize the app. Use the command: cmd.createLayer(); create a layer called unitDemo.style. This becomes the current layer. Type at the command line:
file: unitDemo/style/UnitConverter.sc
UnitConverter {
   xpad = 5;
   ypad = 5;
   gap = 10;
   foreground = Color.WHITE;
   background = Color.BLACK;
   errorLabel {
     foreground = Color.RED;
   }
}

Compiled Layers

These layers use dynamic mode which for StrataCode means interpreted code, incremental updates of types and instances in most cases, keeping the safety of strong type checking.

When you need performance, simply remove the "dynamic" keyword from the class or layer, and StrataCode compiles, rather than interprets, the definition. With no dynamic layers, your code is not dependent on the StrataCode language engine. A standard jar file is produced with only a few simple utilities required from StrataCode. When dynamic layers are present, you need the dynamic layers in the "layer path" of the runtime, plus the small/portable StrataCode language engine.

Edit the files: unitDemo/model/model.sc, unitDemo/extendedModel/extendedModel.sc, unitDemo/swingui/swingui.sc and remove the dynamic keyword from these layer definition files. Run: scc unitDemo/swingui Your application runs now in compiled mode. Look at the generated java files in unitDemo/swingui/build. Run the command unitDemo/swingui/build/swingmain to run the application without the sc command. Note: making a layer dynamic implies that all layers which extend that layer are themselves dynamic.

Command Interpreter

A few more commands that might be helpful at this point:
    cmd.down();
    cmd.up();
Move down or up to the next layer down in the list. When inside a type, you move to the next layer which modifies that type. When at the root level, you move to the next layer in the global stack of layers. The # characters before and after the type name indicate the number of layers above and below the current layer in the stack. Use these commands:
    cmd.print();  // prints the current type
    cmd.listObjects();  // lists sub-objects of this type
    cmd.list();  // lists all members of this type
    cmd.print("xx");  // prints details of the named member
    cmd.edit();    // Valid only when a type is opened.  Edits the file, saves, and refreshes if the file changed.
To see all commands, simply use cmd. followed by the TAB character. Command-line completion works in many, not all contexts. Also use up-arrow/down-arrow to navigate through your command history. Next, add a UnitConverter web UI with the [wicket framework](wicket.html) mobile using [android](gettingStartedAndroid.html), or javascript application using [gwt](gwt.html).