DevelopingTypes - gama-platform/gama GitHub Wiki
GAML has built-in data types (int, float, point, list, geometry, date...). A plugin can add new ones. For example, gama.extension.kml adds the kml type, gama.extension.image adds image, and gama.extension.dataframe adds dataframe. Once declared, a type can be used like any other type:
kml k <- kml(nil); // casting operator generated automatically
list<kml> exports; // as content type of a container
A new type needs two Java classes:
- the value class: the Java class of the values of the type (e.g.
GamaKmlExport); - the type class: a subclass of
GamaTypeannotated with@type, which describes the type to GAML and converts other values into it (e.g.GamaKmlExportType).
The annotation processor (gama.processor) registers the type in the plugin's generated gaml/ folder (class gaml.additions.<shortcut>.GamlAdditions). Never edit that code by hand. See Developing Plugins for the plugin set-up.
classDiagram
class GamaType~Support~ {
<<abstract>>
+cast(IScope, Object obj, Object param, boolean copy) Support
+getDefault() Support
+canCastToConst() boolean
+deserializeFromJson(IScope, IMap) Support
}
class GamaKmlExportType {
@type(name = "kml")
}
class GamaKmlExport
GamaType <|-- GamaKmlExportType
GamaKmlExportType ..> GamaKmlExport : wraps
The value class can be any Java class. The KML example uses a plain class (gama.extension.kml/src/gama/extension/kml/GamaKmlExport.java). However, a value class should implement gama.api.types.misc.IValue (in gama.api), so that GAMA can display, copy and save its values:
-
String stringValue(IScope scope): the text shown to the user; -
IValue copy(IScope scope): a copy, used bycopy(...)and by casts withcopy = true; -
IType<?> getGamlType(): the GAML type of the value; -
String serializeToGaml(boolean includingBuiltIn): a GAML expression that rebuilds the value; -
serializeToJson(IJson json): the JSON form of the value.
To let modelers write my_value.name, declare the attributes on the value class (or on its interface) with @vars/@variable and give each one a @getter (and optionally a @setter). For example, gama.api/src/gama/api/types/color/IColor.java declares the attributes of colours:
@vars ({ @variable (name = IKeyword.COLOR_RED, type = IType.INT, doc = { @doc ("Returns the red component of the color (between 0 and 255)") }),
... })
public interface IColor extends IValue, ... {
@getter (IKeyword.COLOR_RED)
default Integer red() { ... }
...
}A getter on a value class has no parameter, or only an IScope. See @vars, @getter and @setter.
The type class extends gama.api.gaml.types.GamaType<Support>, where Support is the value class. It is annotated with @type. Here is the complete type class of kml (gama.extension.kml/src/gama/extension/kml/GamaKmlExportType.java):
@type (
name = "kml",
id = GamaKmlExportType.ID,
wraps = { GamaKmlExport.class },
kind = ISymbolKind.REGULAR,
concept = { IConcept.TYPE },
doc = { @doc ("Type of variables that enables to store objects and to export them into a KML (Keyhole Markup Language) file") })
public class GamaKmlExportType extends GamaType<GamaKmlExport> {
static final int ID = IType.BEGINNING_OF_CUSTOM_TYPES + 29;
public GamaKmlExportType(final ITypesManager typesManager) {
super(typesManager);
}
@Override
public boolean canCastToConst() { return true; }
@Override
@doc ("Returns a kml exportation object if the argument is alrady of type kml, otherwise nil")
public GamaKmlExport cast(final IScope scope, final Object obj, final Object param, final boolean copy)
throws GamaRuntimeException {
if (obj instanceof GamaKmlExport) return (GamaKmlExport) obj;
return null;
}
@Override
public GamaKmlExport getDefault() { return new GamaKmlExport(); }
}The processor checks that the class implements IType, and generates the code that creates it with new GamaKmlExportType(typesManager). The constructor taking an ITypesManager is therefore required.
| Attribute | Default | Use |
|---|---|---|
name |
(required) | The GAML keyword of the type. |
id |
(required) | A unique integer id (see below). |
wraps |
(required) | The Java classes of the values. The first one is the preferred one. GAMA uses these classes to find the GAML type of a Java value, for example the return type of an operator. |
kind |
ISymbolKind.REGULAR |
REGULAR for most types, NUMBER for numeric types. |
concept |
{} |
Keywords from IConcept, used by the documentation. |
category |
{} |
Categories used by the documentation. |
internal |
false |
Hides the type from the documentation. |
doc |
{} |
The documentation of the type. |
The ids of the built-in types are constants of gama.api.gaml.types.IType (IType.INT = 1, IType.FLOAT = 2, IType.GEOMETRY = 13, IType.DATE = 23...). GAMA uses these ranges:
| Range | Used for |
|---|---|
| below 5000 | built-in types of GAMA |
from IType.BEGINNING_OF_CUSTOM_TYPES (5000) |
types defined in plugins |
from IType.BEGINNING_OF_SPECIES_TYPES (10000) |
types created for species, numbered automatically |
from IType.BEGINNING_OF_FILE_TYPES (20000) |
types created for @file declarations, numbered automatically |
Define the id of your type as IType.BEGINNING_OF_CUSTOM_TYPES + n, with an n that is not used by another plugin. Search for BEGINNING_OF_CUSTOM_TYPES + in the workspace to see the values already taken (for example 29 for kml, 30 for image, 35 for dataframe). The old constant AVAILABLE_TYPE and the old advice to use ids above SPECIES_TYPE are obsolete.
-
Support cast(IScope scope, Object obj, Object param, boolean copy)(abstract): converts any value into a value of the type. It returnsobjitself if it is already of the type (or a copy ifcopyistrue), builds a new value when a conversion makes sense, and returnsnullotherwise.paramis an optional parameter of the cast. Document this method with@doc: every type automatically gets a casting operator with the name of the type (kml(x)), and that operator callscast. The processor reports a warning if this documentation is missing. -
Support getDefault(): the default value of variables of this type (nullmeansnil). -
boolean canCastToConst():trueif a cast of a constant can be computed at compile time. -
Support deserializeFromJson(IScope scope, IMap<String, Object> map2)(optional): rebuilds a value from its JSON form, when the value class implementsserializeToJson.
GamaType has other methods that you can override, for example to declare a content type or a key type for container-like types. Container types extend GamaContainerType instead (see gama.extension.dataframe/src/gama/extension/dataframe/GamaDataFrameType.java).
Operators can use the value class as parameter or return type: GAMA finds the GAML type through wraps. If the Java return type is not precise enough, set type = <your id> in @operator. You can also put @operator directly on methods of the value class: the value then becomes the first operand. See Developing Operators.
The GAMA developer tools provide the wizard GAMA > Create GAMA artefacts > New GAMA Type (also in File > New > Other... > GAMA Development). It "creates a new GAML type and the Java class of its values". It asks for the plugin, the package, the class name (default MyThingType) and the type name. It generates:
-
MyThingType extends GamaType<MyThing>, with the@typeannotation, the constantsNAMEandID = IType.BEGINNING_OF_CUSTOM_TYPES + <offset>,cast(with copy support),getDefault()(returningnull),canCastToConst()(returningtrue) anddeserializeFromJson; -
MyThing implements IValue, with anameattribute declared with@varsand@getter("name").
The offset of the id is computed from the name of the type. If it collides with the id of another type, change it.
If your values are structured objects with attributes and actions, you can declare a GAML class instead of a type: annotate a Java class implementing IObject with @gaml_class. Modelers can then create instances with my_class(...), use them as types, and even inherit from them in GAML with class ... parent: .... See Developing Species and the migration guide.