- Introduction
- Syntax Arguments
- PropDef, FieldPropDef, and Custom PropDefs
- Initial Values
- Overriding PropDef Methods
- PropDef Arguments
- Caching PropDefs
- Scopes and Blocks
Introduction
There are two ways to append PropDefs into a PropObj.
Traditionally, appendPropDefs(PropDefs defs)
is used for creating and appending PropDefs:
/** * Append PropDefs. */ public void appendPropDefs(PropDefs defs) { super(..); // Create and append PropDefs here... }
Another is the newer(2019) Append PropDefs syntax of doing so:
/** * Append PropDefs. */ public props { // What PropDefs to create goes here... }
Which actually compiles into exactly overriding these two methods:
/** * Internal append prop defs. * This should never be overridden outside of the props syntax. */ public void internalAppendProps(PropDefs defs) { // Compiled statements from props syntax... } /** * Init props. * This is where init {} code goes as well as value initializations. */ public void internalInitProps(PropDefs defs) { // Initialization code from the syntax compiles to here... }
appendPropDefs(PropDefs defs)
can be used in conjunction with the PropDef syntax and is always an option instead.
The syntax for appending PropDefs to PropObjs is quite beneficial. There is no longer a need to experience the nuances of using the traditional appendPropDefs()
. (Remember PropDef classes to instantiate, differences between PropDefs vs FieldPropDefs, caching syntax for utilizing the propDefCache, attributes for copy=(null, shallow, or reference) or for stream=null, methods to append with, etc...)
In general, the syntax allows for the most ease in defining PropDefs to reduce the clutter of all that is needed. Below is an outline of the syntax and how it should be used within PropObjs in comparison to using appendPropDefs()
assuming there is a boolean field named boolProp
.
/** * Append PropDefs. */ public props { "boolProp"; }
/** * Append PropDefs. */ public void appendPropDefs(PropDefs defs) { defs.put(FieldPropDef(this.class, "boolProp")); }
Syntax Arguments
The syntax can have special arguments passed in:
/** * Append PropDef syntax. */ public props : debug=true, super=false { // What PropDefs to create goes here... }
There are currently only 4 special arguments that can only be placed here:
Argument | Description |
debug=true | When specified, a dump of what is being produced in the syntax symbol table will be displayed on compile. False by default or if not in developMode. |
fullDebug=true | A complete dump for the symbol table as well as a printout of the statements within each method will be displayed on compile. False by default or if not in developMode. |
help=true | Prints out an example of using this syntax as well as a link to get to this wiki page. False by default or if not in developMode. |
super=false | Keep in mind the syntax is essentially overriding the internalInitProps() and internalAppendProps() method. Therefore this argument means it will not call super in the overridden methods.Else it calls the super method by default. |
Do note the syntax DOES NOT recognize placing in dynamic fields as the boolean argument.
Meaning this would not be recognized and default to its default value, which is super=true
.
/** * Should I call super? */ public bool callSuper = false;
/** * Append PropDef syntax. */ public props : super=callSuper { // What PropDefs to create goes here... }
PropDef, FieldPropDef, and Custom PropDefs
They help define the key/value pairs inside the PropObj.propData str->Object map. Each PropDef must have both a k:str member and a pType:Type member, where k equates to the key:str within the propData and the type defines what the value type is/can be (See also PropObj). They should never exist outside of a PropObj (except for the cache -- more on that later) and they should only be accessed via a PropObj.
Different classes of PropDefs can be made to suit different needs and generally fall into the base PropDef class, FieldPropDef, and other custom PropDef classes. Below is an example of this in action assuming there is a boolean field in the class named boolFieldPropDef
.
/** * Boolean field property. */ public bool boolFieldPropDef;
/** * Append PropDefs. */ public props { int "basePropDef"; double "customPropDef" : ExampleCustomPropDef; "boolFieldPropDef"; // OR "boolField" : fieldName="boolFieldPropDef"; }
If a data type is given using the syntax, it'll initialize the PropDef as the base PropDef class. Unless stated like how "customPropDef" is initialized by passing in the ExpCustomPropDef class.
If the type is not stated and a field exists of the same name, it'll automatically be initialized as a FieldPropDef. Alternatively, you can also assign the PropDef key to a different name and still tie the actual field name from the class as such.
This is how it looks like doing the exact same if it's done traditionally instead:
/** * Append PropDefs. */ public void appendPropDefs(PropDefs defs) { defs.put(PropDef("basePropDef", int)); defs.put(ExampleCustomPropDef("customPropDef", double)); defs.put(FieldPropDef(this.class, "boolFieldPropDef")); }
Initial Values
We can initialize values associated with a PropDef.
This adds a put(..)
within the internalInitProps(..)
that will set a starting value.
/** * Props. */ public props { bool "myBool" v=true; double "myDouble" v=34 : domain=myDoubleSubSet(); }
Overriding PropDef Methods
One special thing the append PropDef syntax can do is overriding PropDef methods within the syntax itself automatically and not needing to manually create a subclass of the PropDef.
/** * PropDef example class. */ public class PropExample extends PropObj { /** * Integer field property. */ public int intFieldPropDef; /** * Append PropDefs. */ public props { "intFieldPropDef" : { Object get(PropObj owner, Object env) { return 42; } void put(PropObj z, Object v, Object env) { that.printSomething(v); } } } /** * Print something. */ extend public void printSomething(Object v) { pln("Magic! ", v); } } { PropExample exp(); for (k, def in exp.propDefs.defs) { pln(k); pln(def); pln(exp.get(k)); exp.put(k, 89); } }
intFieldPropDef @ExpDefintFieldPropDef2509(int intFieldPropDef []) 42 Magic! 89
When a method override is placed in the syntax, the PropDef will automatically be subclassed as if it's a private class in the same file of this current class PropExample
. Notice the way it's named contains the key the PropDef (@ExpDef + "your PropDef key" + a unique number of the auto subclassed PropDef):
Object -- cm.lang PropDef -- cm.props FieldPropDef -- cm.props @ExpDefintFieldPropDef2509 : private -- profile.configura
Notice also there is a special that
field within the override. that
is a reference to the owner of the PropDef (for this example, it would be PropExample). Since this is essentially a method override of a PropDef class, this
would refer to the overridden PropDef class, so we use that
to reference the owner. The casting of that
from PropObj to the actual owner subclass is also done automatically.
The usual case for using this is overriding put()
and get()
. This essentially is like the getter and setter methods of a normal field, but for a "field"(key and value pair) in the propData str->Object map.
Method overrides in the syntax can also be placed in a block to form a scope (see the Scopes and Blocks section of this article).
PropDef Arguments
Within the syntax creating PropDefs, we can pass in arguments to further customize the initialization of the PropDef. Example:
/** * Append PropDefs. */ public props : cached=false { "sample"; "example" : cached=true, stream=null; defs cached=true { "exampleInDefsBlock1"; "exampleInDefsBlock2"; } }
Observe that any PropDef argument(cached
in this example) can be placed on 'props' as if it's a syntax argument, this means all PropDef initialization will be set to false unless stated otherwise.
The example here will then be interpreted as:
- "sample" is not cached.
- "example is cached and this PropDef cannot be streamed or "saved".
- "exampleInDefsBlock1" and "exampleInDefsBlock2" will be cached as a result of being inside a defs block (see the Scopes and Blocks section of this article) which states the cached argument as true.
List of arguments that can be placed on 'props', inside a def block, or on individual PropDefs:
Argument | Description |
attributes=symbol{} | The 'attributes' argument in the constructor. Does not make the def a 'core' type. |
domain=Object | The 'domain' argument of the CorePropDef constructor. Makes the def of 'core' type. |
default=Object | The 'default' argument of the CorePropDef constructor. Makes the def of 'core' type. |
setting=Object | The 'setting' argument of the CorePropDef constructor. Makes the def of 'core' type. |
fieldName=str | The 'fieldName' argument of the CoreFieldPropDef constructor. Makes the def of 'core' type. |
cached=bool | Is the def to be cached within the propDefCache? DOES NOT accept a dynamic field and must be assigned a static true or false value. |
stream=null | Puts #stream_null in the PropDef attributes. |
copy=null | Puts #copy_null in the PropDef attributes. |
copy=reference | Puts #copy_reference in the PropDef attributes. |
copy=shallow | Puts #copy_shallow in the PropDef attributes. |
pipeKey=str | The reference to a PropObj in propData where this def redirects its calls. |
exposed=bool | If true and this value is a PropObj, the PropObj will expose its PropDefs to this PropObj via PropDefs with pipeKey. |
expose=defs | If this def were to be exposed, it can only be done by another PropDef with a pipeKey to this. |
expose=readOnly | If this def were to be exposed, put(..) will not be considered for piping. |
expose=pipe | If this def were to be on a PropObj who is returned by PropObj.pipe():PropObj, on get() it will be considered. |
expose=private | This def may not be accessed or set by any other PropObj with or without exposure. |
sym=bool | If true, the instantiated type of the PropDef will be of type SymParamPropDef. |
Caching PropDefs
PropDefs may be cached so that their creation later may be quicker. For PropObjs that are created often, this results in a quicker construction. In the case of FieldPropDefs, the savings are quite noticeable as the field lookups are avoided if the entry already exists within the cache.
It is important to note that PropDefs with changing 'domain', 'setting', or 'default' fields should often be kept out of the cache as these PropDefs may be pointed to by other PropObjs as well. Such situations include quick properties for example as the PropDef 'setting' may be altered by individual instantiations of that PropObj differently.
appendPropDefs PropDef cache syntax (the long way):
/** * Append prop defs. */ public void appendPropDefs(PropDefs defs) { super(..); // Put a PropDef into the cache -- if the entry already exists, just append it to defs. cacheProp("myProp", bool, defs) { result PropDef("myProp", bool, default=true); }; // Put a FieldPropDef into the cache. cacheProp("myField", defs) { result FieldPropDef(this.class, "myField", fieldName="myFieldName", default=true); }; }
append props syntax (results are the same as above -- super called unless super=false
supplied)
// Placed in props args public props : cached=true, default=true { bool "myProp"; "myField" : fieldName="myFieldName"; } // Or placed on individual prop public props { bool "myProp" : cached=true, default=true; "myField" : cached=true, default=true, fieldName="myFieldName"; } // Or placed on a defs container public props { defs cached=true, default=true { bool "myProp"; "myField" : fieldName="myFieldName"; } }
Scopes and Blocks
Another neat trick this syntax can do is applying something to a scope of PropDefs by placing them in a block of code.
defs block
The defs block is meant to simply make it easier to define attributes and types over many PropDef definitions. In practice, nested 'defs' should be avoided as much as possible -- keeping the code easy to read.
/** * Append PropDefs. */ public props { defs { // Definitions behave normally.. "myProp"; } defs MyCorePropDef { // Definitions are of type MyCorePropDef unless otherwise specified.. defs MyOtherCorePropDef { // Definitions are of type MyOtherCorePropDef unless otherwise specified.. } defs PropDef core=false { // Definitions are of type PropDef unless otherwise specified.. } } defs domain=true, setting=true { // Definitions are of type 'core' and have domain=true and setting=true.. defs domain=false { // Definitions are of type 'core' and have domain=false and setting=true.. } } defs MyCorePropDef cached=true, default=true { // Definitions are of type MyCorePropDef, are cached, and have default=true.. } }
methods block
The 'methods' block takes all PropDefs defined within the scope and overrides all of its methods. This can be very nice to avoid writing out the same implementation many times for multiple PropDefs that require the same behavior.
/** * Props. */ public props { //Normal FieldPropDefs and PropDefs. "myFieldPropDef"; int "myPropDef"; //Implement methods for myFieldPropDef and myPropDef. methods { str label(..) { return "myFieldPropDef and myPropDef"; } } // MyCoreProps. defs MyCoreProp default=true { bool "myCoreProp0"; bool "myCoreProp1"; bool "myCoreProp2"; // Implement methods for all MyCoreProps. methods { Object default(..) { return env ? default = env : default; } str label(..) { return "MyCoreProp"; } } } }
PropDef key block
This works exactly like the method block(above) in overriding PropDef methods, but you can specify which PropDef on overriding the method rather than all the methods in the current scope.
/** * Append PropDefs. */ public props { bool "myProp"; int "myOtherProp"; "myProp" { Object default(..) { return true; } } "myProp", "myOtherProp" { str label(..) { return "mine"; } } // Can also be done without " " myProp { Object domain(..) { return env; } } }
construct block
A 'construct' block is a block(s) within the syntax that is executed before any of the PropDefs are created. It compiles to statements executed in the internalAppendProps(..)
.
/**
* Append PropDefs.
*/
public props {
construct {
// Anything here will be executed before anything is created (after super(..) if !super=false)..
// Ex. appending a prop the old way ('defs' is in scope as we are within internalAppendProps(..))
defs.put(MyMadeUpPropDef("someKey", bool));
//fields declared here can be used within the syntax.
int val9 = 9;
PropInputSetting generalSetting();
}
defs setting=generalSetting {
"myIntFieldPropDef" : default=val9;
"myOtherIntFieldPropDef" : default=val13;
// Just for example -- these can be placed ANYWHERE.
// The statements are also appended top-down.
construct {
int val13 = 13;
}
}
// Create the PropDefs
bool "myPropDef";
construct {
// Anything here or within any other construct block will be appended to the statements to be
// executed before anything is created (in order of compilation -- top down). This includes containers.
}
}
One can use these blocks to append PropDefs the old way (defs.put(PropDef(..))
) or to initialize other objects used within the construction of the PropDefs. An example of this might be exposed=true
-- one might want to ensure that the PropObj being exposed exists.
Fields declared in this block can be referred to within the syntax.
It is important to note that anything within the block is omitted from props interpretation or manipulation. Anything in the block will not be altered by the syntax (Ex. default=Object
etc.. specified within containers).
init block
An 'init' block(s) contains code to be executed after the PropDefs are created (internalInitProps(..)
).
/**
* Append PropDefs.
*/
public props {
init {
// Any code within this block will be executed after PropDef creation..
}
// Create prop defs..
init {
// Any code here will be executed after creation and appended below the statements in previous init blocks.
// Ex. initializing def values (can be done with the v= as well)
this."myProp" = someValue;
propDef("otherProp").domain = otherSubSet();
// Any code that directly relates to these PropDef initializations
}
}
Just like with the 'construct' blocks, anything within the 'init' will be omitted from props interpretation -- separate from the syntax entirely.
See also a PropDef syntax presentation YouTube video.
Comments
0 comments
Please sign in to leave a comment.