X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=inline;f=docs%2FDeveloper%2FDatabase%2FVariable.md;fp=docs%2FDeveloper%2FDatabase%2FVariable.md;h=d33dbb9bd4aa9aa6183523a972f71e44842fa559;hb=a9ec58f08ccc02e65b1cab6aedff25e0cf3c6444;hp=0000000000000000000000000000000000000000;hpb=5998374f7e179cfaf451c220216adc18c823047f;p=simantics%2Fplatform.git diff --git a/docs/Developer/Database/Variable.md b/docs/Developer/Database/Variable.md new file mode 100644 index 000000000..d33dbb9bd --- /dev/null +++ b/docs/Developer/Database/Variable.md @@ -0,0 +1,473 @@ +# Intro + +The Variable interface provides an uniform access model to data in Simantics. It includes + +* Tree-structured address space for accessing model **structure** and **properties** +* Uniform access to model **configuration** and **state** + +Key use cases include + +* Browsing of the **model configuration and states** (see [[Model Browser]]) +* **Manipulation of objects** (see [[Selection View]]) +* Representation of **tabular data** (see [[Spreadsheets]]) + +Main functional requirements include representation of + +* **Structural models** with **procedural** features (see [[Structural]]) +* **Runtime** data of solvers (see [[Experiments]]) +* **Historical** data from experiments +* **Ontological** data + +# Solution + +**Variable** is a tree-structured view into the Simantics data model. Each variable is either a **child** or a **property** and can further contain own children and properties. The difference between a child and a property is that a property contains a **value**. + +The variable space browsing methods are used to obtain + +* all children +* children by name +* all properties +* properties by name +* variable by **path** +* **parent** variable + +Other services are + +* accessing (get/set) the value of a property variable +* querying adapter interfaces + +A set of built-in properties is required for all variables. These properties have also dedicated interface methods. + +* **URI**, which is an unique string identifier and locator of the variable in the tree structure +* **Parent**, which is the tree parent of the variable +* **HasName**, which is a local identifier for the variable within its parent. Names are also used to create URIs. +* **HasLabel**, which is a short textual representation of the variable +* **hasStandardResource**, which returns the context resource in a **standard graph-based child variable** +* **Represents**, which is a resource representing the variable **TODO** +* **Type**, which returns a single type resource classifying the variable +* **Role**, which tells whether the variable is a **child** or a **property** (TODO: could be deprecated) +* **DATATYPE**, which returns the data type of a property variable. (TODO: should be HasDatatype) + +Other properties and the structure of the variable space is configured in the semantic graph or contributed by custom variable implementations. + +Variables can be located using an **URI**, which + +* Represents the path from **root variable** (Variables.getRootVariable) into the variable such that +** **var/xx** represents a getChild(unescaped(xx)) query from var +** **var#yy** represents a getProperty(unescaped(yy)) query from var +** the escape function is bidirectional (URIStringUtils.escape and URIStringUtils.unescape) +* Is an **identifier** (two variables with the same URI are the same in the sense of Java Object.equals) +* Is a random access identifier (by Variables.getVariable()) +* Examples: +** http://www.acme.org/Projects/MyProject/MyModel/Configuration/DiagramN/PI_X#PI_MASS_FLOW +** http://www.acme.org/Projects/MyProject/MyModel/ExperimentConfiguration/RunName/DiagramN/PI_X#PI_MASS_FLOW#DATATYPE + +A common way of identifying a variable is by supplying a **base variable** and a **Relative Variable Identifier (RVI)**. + +* RVI represents the path from **base variable** into another variable +* In textual RVI notation (Variable.browse()) +** **.** represents a getParent() query +** **/xx** represents a getChild(unescaped(xx)) query +** **#yy** represents a getProperty(unescaped(yy)) query +* A literal RVI (Variable.getRVI(), RVI.resolve()) +** Does not need to depend on the names visible in the URI +** Is based on e.g. resource ids +** Survives export/import + +A **model** variable represents the root of a Simantics model +* Model variables correspond directly to instances of SIMU.Model in the database +** Variable and resource URIs are the same +* For all variables under a model, the model variable can be obtained using Variables.getModel() + +A **context** variable under a model provides a view into a **state** of the model +* The **Type** property of a context variable is inheried from L0.RVIContext +* A RVI obtained from e.g. model configuration can be used to access similarly identified data from different model states +** E.g. /DiagramX/ComponentY#PropertyZ can have different values in different contexts +* The **configuration** context can be used to browse the structure and configuration values of the model +* **Experiment run** contexts are used to monitor values from simulations or history + +The variable interface is bound to Simantics database **transactions**, but is not in any other way bound to the semantic data model, which allows variable implementations to represent arbitrary data models somehow related to Simantics models. All variable-based requests can be listened using standard Simantics database listening. + +Procedural children and variables are used with large data sets. E.g. query-based views can be exposed. Procedural properties also enable efficient slicing of arrays e.g. URI#Array_Property/0-99 + += General assertions in the Variable model = + +* All variables except the **root** have a parent +* Let p be the parent of v. Then v#URI equals p#URI + '/'|'#' + escape(v#HasName) +* Iff v1#URI equals v2#URI, then v1 and v2 are equal in Java Object.equals sense +** Other identifications can be established by property values +* A variable v2 equaling variable v can always be obtained by calling Variables.getVariable(v#URI) +** The obtained variable **need not be the same object** but **can be** +** Variables.getVariable can return also variables, which are not reachable by browsing (**TODO**) +* All property variables have a value +** No child variable has a value +** The value of a property variable may be **null** +** The value of **DATATYPE** property can be null for property variables i.e. property values can be arbitrary Java objects +* Variable.getProperty returns all the variables returned from Variable.browseProperties +** Variable.getProperty can return variables not returned by Variable.browseProperties +* Variable.getChild returns all the variables returned from Variable.browseChildren +** Variable.getChild can return variables not returned by Variable.browseChildren +* A variable can be part of at most one **model** +* A variable can be part of at most one **context** +* All values can be accessed using either Variable.getValue or Variable.getInterface(Accessor.class) +* All properties retrieved using Variable.browseProperties shall be available using Variable.getProperty +* No variable can have a name that begins with one or more dots ('.') due to '.' being a reserved character for browsing the variable address space towards the parent. In other words names matching the pattern **"^\.+.*$"** are forbidden. + +# Standard properties + +## Connections + +* Connection point properties are classified with **http://www.simantics.org/Structural-1.0/ConnectionRelation** +* The value of a connection point property is an object of class **org.simantics.structural2.variables.Connection** + +~~~ + +public interface Connection { + + Collection getConnectionPoints(ReadGraph graph) throws DatabaseException; + +} + +~~~ + +It is assumed that instances of **org.simantics.structural2.variables.Connection** have proper identities (equals/hashCode) based on flattened connections. + +The value of connection point properties can be **null**. This means that the connection point is not connected. + +## String editing operations + +* **HasDisplayValue** is a String-valued property, which is a formatted and unit-converted representation of the property value. +* **expression** is a String-valued property, which is an SCL-formula used to compute the value of the property. If the property can be computed using an expression, this property is always available and returns **null** if an expression has not been defined. +* **validator** is a **org.simantics.utils.strings.StringInputValidator**-valued property. The validator is used for checking values to be written into a property. + +~~~ + +public interface StringInputProblem { + + enum Severity { + Error, Warning + } + + String getDescription(); + int getBegin(); + int getEnd(); + Severity getSeverity(); + +} + +public interface StringInputValidator { + + Collection validate(String input); + +} + +~~~ + +* **valid** is a Boolean-valued property, which indicates whether the property contains a valid value. + +## Property properties + +* **required** is a Boolean-valued property, which indicates that the property should contain a valid value +* **default** is a Boolean-valued property, which indicates that the property value is a default value +* **readOnly** is a Boolean-valued property, which indicates that the property value can not be written + +## Complex datatypes + +* Record + * All named fields are '/name' + * Tuples are named after position e.g. '/11' +* Union + * Union does not show in URI +* Array + * Elements are named after position e.g. '/i-11' +* Map + * Items are named after key + +# Standard graph based variable implementation + +The standard child and property variables are + +* org.simantics.db.layer0.variable.StandardGraphChildVariable +* org.simantics.db.layer0.variable.StandardGraphPropertyVariable + +Their implementation is based on the following interfaces + +~~~ + +package org.simantics.db.layer0.variable; + +public interface VariableMap { + Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException; + // Must not modify collection in any way not possible with put-method. + void getVariables(ReadGraph graph, Variable context, Map map) throws DatabaseException; +} + +~~~ + +~~~ + +package org.simantics.db.layer0.variable; + +public interface ValueAccessor { + Object getValue(ReadGraph graph, Variable context) throws DatabaseException; + Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException; + void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException; + void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException; +} + +~~~ + +Implementations of the above interfaces can be bound to instances and types in the database via the following standard properties + +~~~ + +L0.Entity + >-- L0.hasStandardResource ==> "Resource" -- L0.domainProperties ==> "VariableMap" -- L0.domainChildren ==> "VariableMap" -- L0.valueAccessor ==> "ValueAccessor" modifier = context.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER); + if(modifier == null) modifier = VariableUtils.defaultInputModifier; + try { + modifier.apply(graph, context, value, Bindings.getBinding(value.getClass())); + } catch (BindingConstructionException e) { + throw new DatabaseException(e); + } + } + @Override + public void setValue(WriteGraph graph, Variable context, Object value, Binding binding) throws DatabaseException { + StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; + ValueAccessor propertyAccessor = getPossiblePropertyValueAccessor(graph, variable); + if(propertyAccessor != null) { + propertyAccessor.setValue(graph, context, value, binding); + return; + } + Function4 modifier = context.getPossiblePropertyValue(graph, Variables.INPUT_MODIFIER); + if(modifier == null) modifier = VariableUtils.defaultInputModifier; + modifier.apply(graph, context, value, binding); + } +}; + +@SCLValue(type = "VariableMap") +public static VariableMap standardChildDomainProperties = new VariableMap() { + + @Override + public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException { + final StandardGraphChildVariable variable = (StandardGraphChildVariable)context; + return getPossiblePropertyFromContext(graph, variable, variable.resource, name); + } + @Override + public void getVariables(ReadGraph graph, Variable context, Map map) throws DatabaseException { + StandardGraphChildVariable variable = (StandardGraphChildVariable)context; + collectPropertiesFromContext(graph, variable, variable.resource, map); + } + +}; + +@SCLValue(type = "VariableMap") +public static VariableMap standardPropertyDomainProperties = new VariableMap() { + + @Override + public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException { + StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; + Resource literal = graph.getPossibleObject(variable.parentResource, variable.property); + if(literal != null) { + Variable result = getPossiblePropertyFromContext(graph, variable, literal, name); + if(result != null) return result; + } + return getPossiblePropertyFromContext(graph, variable, variable.property, name); + } + @Override + public void getVariables(ReadGraph graph, Variable context, Map map) throws DatabaseException { + StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; + collectPropertiesFromContext(graph, variable, variable.property, map); + Resource literal = graph.getPossibleObject(variable.parentResource, variable.property); + if(literal != null) collectPropertiesFromContext(graph, variable, literal, map); + } + +}; + +@SCLValue(type = "VariableMap") +public static VariableMap standardChildDomainChildren = new VariableMap() { + + @Override + public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException { + StandardGraphChildVariable variable = (StandardGraphChildVariable)context; + Map children = graph.syncRequest(new UnescapedChildMapOfResource(variable.resource)); + Resource child = children.get(name); + if(child == null) return null; + return graph.getPossibleContextualAdapter(child, variable, Variable.class, Variable.class); + } + @Override + public void getVariables(ReadGraph graph, Variable context, Map map) throws DatabaseException { + StandardGraphChildVariable variable = (StandardGraphChildVariable)context; + for(Map.Entry entry : graph.syncRequest(new UnescapedChildMapOfResource(variable.resource)).entrySet()) { + String name = entry.getKey(); + Resource child = entry.getValue(); + Variable var = graph.getPossibleContextualAdapter(child, variable, Variable.class, Variable.class); + if(var != null) { + map.put(name, var); + } else { + System.err.println("No adapter for " + child + " in " + variable.getURI(graph)); + } + } + } + +}; + +@SCLValue(type = "VariableMap") +public static VariableMap standardPropertyDomainChildren = new VariableMap() { + + @Override + public Variable getVariable(ReadGraph graph, Variable context, String name) throws DatabaseException { + StandardGraphPropertyVariable variable = (StandardGraphPropertyVariable)context; + Datatype dt = variable.getDatatype(graph); + if (dt instanceof ArrayType) { + ChildReference ref = getPossibleIndexReference(name); + if (ref != null) + return new SubliteralPropertyVariable(variable, ref); + } + return null; + } + @Override + public void getVariables(ReadGraph graph, Variable context, Map map) throws DatabaseException { + } + +}; + +~~~ + +## Informally + +Standard child modelling assumes a context resource res. + +* childResource : where (res, L0.ConsistsOf, childResource) +* childVariable = graph.adaptContextual(childResource, this, Variable.class, Variable.class) + +Standard property modelling assumes a context resource res. + +* (predicate, object) : where (res, predicate, object) and (predicate browse entrypoint "./Out#sdf" +* Use . and #-for browsing + * How to browse parent? Explicit function + * Example: (parent entrypoint).Out#sdf + * Special operator for parents (binds stronger than . or #): + * Example: !entrypoint.Out#sdf +* Resolve entrypoints in the context + * Example: Out#sdf + * Local variable definitions may shadow context + +## Experiment modelling + +The modelling of experiment run contexts and the value types of properties in states is still underway. + +# Refactoring + +* add Collection browseProperties(ReadGraph graph, String classification) throws DatabaseException; +* add standard property **classifications**, which returns a set of strings +* deprecate browseChildren, browseProperties => add get