From a9ec58f08ccc02e65b1cab6aedff25e0cf3c6444 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Fri, 8 Dec 2017 12:50:34 +0200 Subject: [PATCH] First test on Simantics documentation using gitbook Only few parts transferred and somehow quickly reformatted for now. Lots of work remaining. refs #7674 Change-Id: I1cf8b6a3da5ec14064f79d0a0c765899be1d8872 --- docs/.gitignore | 2 + docs/.project | 11 + docs/Developer/Data/Databoard.md | 0 docs/Developer/Database/DatabaseTesting.md | 95 ++++ docs/Developer/Database/Functions.md | 15 + docs/Developer/Database/InterfaceSummary.md | 25 + docs/Developer/Database/InverseRelations.md | 7 + docs/Developer/Database/ProceduralValues.md | 71 +++ docs/Developer/Database/ResourceAdaptation.md | 188 +++++++ .../Database/ResourceSerialization.md | 23 + docs/Developer/Database/SubgraphExtents.md | 45 ++ docs/Developer/Database/Undo.md | 72 +++ docs/Developer/Database/Variable.md | 473 ++++++++++++++++++ docs/Developer/Database/VirtualGraphs.md | 121 +++++ docs/Developer/Introduction.md | 11 + .../Ontology/BinaryContainerFormat.md | 14 + docs/Developer/Ontology/GraphCompiler.md | 11 + docs/Developer/Ontology/GraphFileFormat.md | 219 ++++++++ docs/Developer/Ontology/Images/SubPreObj.png | Bin 0 -> 3929 bytes .../Ontology/Images/SubPreObj_chain_01.png | Bin 0 -> 6563 bytes docs/Developer/Ontology/TransferableGraph.md | 55 ++ docs/Developer/Ontology/VersionMigration.md | 63 +++ docs/Developer/Utilities/GenericFileImport.md | 89 ++++ docs/Developer/Utilities/Logging.md | 67 +++ docs/GLOSSARY.md | 7 + docs/Images/Simantics_logo_pile_01.png | Bin 0 -> 746850 bytes docs/README.md | 7 + docs/SUMMARY.md | 68 +++ docs/User/Introduction.md | 9 + docs/book.json | 14 + docs/package.json | 32 ++ docs/styles/website.css | 32 ++ docs/watch.bat | 1 + 33 files changed, 1847 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/.project create mode 100644 docs/Developer/Data/Databoard.md create mode 100644 docs/Developer/Database/DatabaseTesting.md create mode 100644 docs/Developer/Database/Functions.md create mode 100644 docs/Developer/Database/InterfaceSummary.md create mode 100644 docs/Developer/Database/InverseRelations.md create mode 100644 docs/Developer/Database/ProceduralValues.md create mode 100644 docs/Developer/Database/ResourceAdaptation.md create mode 100644 docs/Developer/Database/ResourceSerialization.md create mode 100644 docs/Developer/Database/SubgraphExtents.md create mode 100644 docs/Developer/Database/Undo.md create mode 100644 docs/Developer/Database/Variable.md create mode 100644 docs/Developer/Database/VirtualGraphs.md create mode 100644 docs/Developer/Introduction.md create mode 100644 docs/Developer/Ontology/BinaryContainerFormat.md create mode 100644 docs/Developer/Ontology/GraphCompiler.md create mode 100644 docs/Developer/Ontology/GraphFileFormat.md create mode 100644 docs/Developer/Ontology/Images/SubPreObj.png create mode 100644 docs/Developer/Ontology/Images/SubPreObj_chain_01.png create mode 100644 docs/Developer/Ontology/TransferableGraph.md create mode 100644 docs/Developer/Ontology/VersionMigration.md create mode 100644 docs/Developer/Utilities/GenericFileImport.md create mode 100644 docs/Developer/Utilities/Logging.md create mode 100644 docs/GLOSSARY.md create mode 100644 docs/Images/Simantics_logo_pile_01.png create mode 100644 docs/README.md create mode 100644 docs/SUMMARY.md create mode 100644 docs/User/Introduction.md create mode 100644 docs/book.json create mode 100644 docs/package.json create mode 100644 docs/styles/website.css create mode 100644 docs/watch.bat diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..7068412b5 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_book/ +node_modules/ \ No newline at end of file diff --git a/docs/.project b/docs/.project new file mode 100644 index 000000000..5bb8dc93f --- /dev/null +++ b/docs/.project @@ -0,0 +1,11 @@ + + + platform-docs + + + + + + + + diff --git a/docs/Developer/Data/Databoard.md b/docs/Developer/Data/Databoard.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/Developer/Database/DatabaseTesting.md b/docs/Developer/Database/DatabaseTesting.md new file mode 100644 index 000000000..3bff066dd --- /dev/null +++ b/docs/Developer/Database/DatabaseTesting.md @@ -0,0 +1,95 @@ +## Overview + +The testing framework is contained in the plug-in **org.simantics.db.testing**. + +This section covers a JUnit-based testing framework for making tests, which use the Simantics Database. The framework handles two separate cases + +* POJO JUnit tests, where tests are executed without OSGi and with some configuration restrictions +* OSGi JUnit tests, where tests are executed in OSGi based on the current workspace + +The framework contains + +* Test case templates for extending in org.simantics.db.testing.base +* Generic test cases in org.simantics.db.testing.cases + +## General + +Both testing approaches use similar test suites. Below is an example + +~~~ +public class Tests { + + public static Test suite() throws Exception { + + TestSuite suite = new TestSuite("My suite"); + + //$JUnit-BEGIN$ + suite.addTestSuite(FreshDatabaseTest.class); + suite.addTestSuite(MyTest.class); + + //$JUnit-END$ + return suite; + + } + +} +~~~ + +The first test is used to set up the initial database and upon which the other tests operate. In POJO mode the first test also needs to give some configuration settings as described below. + +## Templates + +### FreshDatabaseTest + +This test can be used to initialize a database with only selected ontologies installed. The test can be run as is or it can be extended to perform tests upon the initialized database. Use getSession() for obtaining the session. + +### ExistingDatabaseTest + +This test can be used to run tests upon an existing database (created e.g. with FreshDatabaseTest). Use getSession() for obtaining the session. + +### WriteReadTest + +This test can be used to run simple write-first-then-read tests upon an existing database. Override methods write and read to perform testing. + +## Running the tests + +To run the tests open a context menu for the suite class (see example above) and + +* To run in POJO, select Run As .. JUnit Test +* To run in OSGi, select Run As .. JUnit Plug-in Test + +The Plug-in tests need to be run as 'all workspace and enabled target plug.ins' and as application '[No Application] - Headless Mode]' + +Note that the launchers can be configured using .launch - files. + +## POJO details + +The POJO testing framework can be set up by running a customized org.simantics.db.testing.cases.FreshDatabaseTest e.g. + +~~~ +public class InitializationTest extends FreshDatabaseTest { + + @Override + public void configure(TestSettings settings) { + + settings.setAdapters(new String[] { + "org.simantics.db.layer0/adapters.xml", + "org.simantics.spreadsheet.graph/adapters.xml" + }); + settings.setOntologies(new String[] { + "/org.simantics.spreadsheet.ontology/graph.tg", + }); + + settings.setWorkspace(new File("..")); + settings.setInitialized(); + + } + +} +~~~ + +## OSGi details + +The Plug-in testing framework applies all adapters found from the workspace. + +org.simantics.db.testing.cases.FreshDatabaseTest can be used to install all available ontologies from the workspace. diff --git a/docs/Developer/Database/Functions.md b/docs/Developer/Database/Functions.md new file mode 100644 index 000000000..1552d9eeb --- /dev/null +++ b/docs/Developer/Database/Functions.md @@ -0,0 +1,15 @@ +# Intro + +This section deals with the following specifications + +* Specification of generic functions applicable in the Simantics framework +* Specification of functions attached to types (methods) +* Specification of functions with side-effects (operations or commands) +* Automatically generated code based on semantic model about functions (stubs) + +## Specification + +* Functions are objects implementing the interface `org.simantics.scl.runtime.function.Function`. +* We have a type `Value` (?) that can be adapted to an SCL value (in particular to a function). +* We have a relation `HasType` (?) Value -> String that specifies the SCL type of the value +* Operations are functions that return an object implementing `WriteResult`. diff --git a/docs/Developer/Database/InterfaceSummary.md b/docs/Developer/Database/InterfaceSummary.md new file mode 100644 index 000000000..bb42d56ed --- /dev/null +++ b/docs/Developer/Database/InterfaceSummary.md @@ -0,0 +1,25 @@ +# Interface summary + +## General + +### org.simantics.Simantics + +This static class can be used to obtain the active Session. + +### org.simantics.db.Session + +The Session interface represents the connection into the semantic database. Implements e.g. `RequestProcessor` and serves various special interfaces via the `getService`-method. + +## Reading and writing + +### org.simantics.db.RequestProcessor + +`RequestProcessor` is a synchronous interface for making read queries and writes to the semantic database. Main implementations are `Session`, `ReadGraph` and `WriteGraph`. + +### org.simantics.db.Read + +The interface Read represents a query, which produces a single result. Main implementations are `ResourceRead*` and `*naryRead`. + +### org.simantics.db.Write + +The interface `Write` represents a modification into the semantic database. Main implementation is `WriteRequest`. diff --git a/docs/Developer/Database/InverseRelations.md b/docs/Developer/Database/InverseRelations.md new file mode 100644 index 000000000..27aafa0b3 --- /dev/null +++ b/docs/Developer/Database/InverseRelations.md @@ -0,0 +1,7 @@ +According to [[:Media:Layer0.pdf|Layer0]] semantics, relations can have inverse relations. For the database and client this means that when claiming new statements (s, p, o) using + WriteGraph.claim(Resource subject, Resource predicate, Resource object) +an inverse statement (o, inv(p), s) will be automatically claimed into the database. Some statements are such that they tend to raise the degree of the resource (amount of incoming/outgoing relations) to very large numbers. The database is not built to efficiently handle such resources. One such relation is ''InstanceOf'' which is a statement that pretty much each and every resource in the database has. It does not have an inverse relation, but considering it had one like ''HasInstance'', here's what would happen. For example, for each ''L0.String'' instance created an inverse statement would be added (L0.String, L0.HasInstance, :L0.String) statement into the database, thereby enlarging the degree of ''L0.String''. When working with larger databases this would most likely become a problem. + +To make it possible to optimize this situation we provide the possibility to skip adding of inverse relations when claiming statements into the database by using + WriteGraph.claim(Resource subject, Resource predicate, Resource inverse, Resource object) +with a null inverse relation argument to create the sensitive statements such as ''InstanceOf''-relations. diff --git a/docs/Developer/Database/ProceduralValues.md b/docs/Developer/Database/ProceduralValues.md new file mode 100644 index 000000000..ec263df13 --- /dev/null +++ b/docs/Developer/Database/ProceduralValues.md @@ -0,0 +1,71 @@ +It is possible to define procedural values in the graph and compute their values using graph interface. + +Currently the methods for computing the values are: +* T getRelatedValue2(Resource subject, Resource relation) throws DatabaseException; +* T getPossibleRelatedValue2(Resource subject, Resource relation) throws DatabaseException; +* T getRelatedValue2(Resource subject, Resource relation, Object context) throws DatabaseException; +* T getPossibleRelatedValue2(Resource subject, Resource relation, Object context) throws DatabaseException; +* T getRelatedValue2(Resource subject, Resource relation, Binding binding) throws DatabaseException; +* T getPossibleRelatedValue2(Resource subject, Resource relation, Binding binding) throws DatabaseException; +* T getRelatedValue2(Resource subject, Resource relation, Object context, Binding binding) throws DatabaseException; +* T getPossibleRelatedValue2(Resource subject, Resource relation, Object context, Binding binding) throws DatabaseException; + +These methods work like getRelatedValue and getPossibleRelatedValue -methods. They follow the given relation from the given subject and then determine the value based on the resource found. If the resource is a literal, then they work exactly like getRelatedValue and getPossibleRelatedValue. They however extend the old functionality in two ways: +* If the found resource is an instance of ExternalValue, then the URI of the resource is used to find a value that is declared using extensions and Java annotations. +* If the found resource is an instance of Value, the mechanism first finds the property ConvertsToValueWith of the value and uses it compute the value. +These two mechanisms are detailed below. + +## External values + +External values are resources in the graph with type ExternalValue. They must have URIs. Let's consider for example + SEL.Functions.inputModifier : L0.ExternalValue +defined in SelectionView -ontology. + +The definitions of the values are declared in the extension point `org.simantics.scl.reflection.binding` like this + +~~~ + + + + + + + + + +~~~ + +The value itself is defined as: +~~~ +public class All { + @SCLValue(type = "WriteGraph -> Variable -> a -> b -> String") + public static String inputModifier(WriteGraph graph, Variable variable, Object value, Object _binding) throws DatabaseException { + ... + } +} +~~~ + +Any static method or field can be declared an external value with `@SCLValue` annotation. The SCL -type of the value is given as an attribute to the annotation. By default the method name and resource name are matched, but the method name can be overridden in the annotation with name attribute. + +## Contextual values + +External values can be used to define complex constants, in particular functions. Contextual values extend the functionality so that the value may depend on the other properties of the original subject resource that was used for finding the value resource. In some cases, even more needs to be known about the context of the computation than just a resource. Therefore some of the `getRelatedValue2` -methods take a context as a parameter. If it is not given, the default context is the subject resource. + +A contextual value is defined as a resource with type `Value` and properties `ConvertsToValueWith`. For example: + +~~~ + L0.List Resource -> Context -> Result + +The second parameter is the resource of the value itself (in this case the linked list) and the context type can be any type. Result type must be the type annotated with `HasValueType`. + +## Value types + +Types of the values must be annotated with relation `HasValueType`. The type is an SCL type in a string format. A relation may require that its range has a certain value type using `RequiresValueType` -property. diff --git a/docs/Developer/Database/ResourceAdaptation.md b/docs/Developer/Database/ResourceAdaptation.md new file mode 100644 index 000000000..2567078fe --- /dev/null +++ b/docs/Developer/Database/ResourceAdaptation.md @@ -0,0 +1,188 @@ +## Problem Statement + +No language can define itself. There have to be extra-language mechanisms for defining basic concepts of the language. For +computers to understand the language, this means some code. Here is a basic example: + +~~~ +public class ShapeFactory { + public static Shape create(Graph g, Resource r) { + ShapeResources sr = ShapeResource.getInstance(g); + if(g.isInstanceOf(r, sr.Rectangle)) { + ... + return new Rectangle2D.Double(...); + } + else if(g.isInstanceOf(r, sr.Ellipse)) { + ... + return new Ellipse2D.Double(...); + } + ... + + throw new IllegalArgumentException("Resource is not a shape or the shape is unsupported."); + } +} +~~~ + +This works as long as there is only a fixed set of shapes. Addition of every new shape needs modification to the code. +We would like to have something like: +* A declaration telling that instances of Shape can be adapted to objects of Shape class. +* For each concrete subtype of Shape, a code creating an object of the corresponding Shape class. +* Declarations connecting subtypes and code snippets. +* Utility that implements the creation pattern. + +## Solution + +Declarations are written into adapters.xml-file that has to be located in the plugin root: + +~~~ + + + + + + + + ... + + + + + + + + + + + + + + + + +~~~ + +Each `...` section defines adaption to given interface. + +`` gives one or more types where all classes implementing the adaption have been inherited from. + +`` defines direct adaptation from instances of given type (**uri**) by construction the specified class (**class**) through Java Reflection API's using static method (**constructor**) if specified and a constructor of the class otherwise whereas `` defines adaptation from instances of given type (**uri**) by using the given adapter (**adapterClass**). The adapter has to implement the following interface: + +~~~ +public interface Adapter { + void adapt(AsyncReadGraph g, Resource r, AsyncProcedure procedure); +} +~~~ + +In addition the adapter may contain this kind of definitions: + +~~~ + +~~~ + +This means that the resource defined by the uri is always adapted to an instance of the given class. + +The main difference between ``/`` definitions and `` definitions is that ``/`` adaptation cannot be used for complex adaptation since both can only be used to adapt to instances of the defined adapter class and nothing else. `` on the other hand can perform more complex analysis and return any instance of the adaptation target interface. + +Defined adapters can be used with the following synchronous methods, where `clazz`-parameter refers to the interface in the adapter declaration: + +~~~ +public interface ReadGraph { + .... + T adapt(Resource resource, Class clazz) + throws AdaptionException, ValidationException, ServiceException; + + T adaptUnique(Resource resource, Class clazz) + throws AdaptionException, ValidationException, ServiceException; + + T getPossibleAdapter(Resource resource, Class clazz) + throws ValidationException, ServiceException; + + T getPossibleUniqueAdapter(Resource resource, Class clazz) + throws ValidationException, ServiceException; + ... +} +~~~ + +For details, see [[svn:db/trunk/org.simantics.db/src/org/simantics/db/ReadGraph.java|ReadGraph interface]]. + +In addition to ``-elements, the file may contain ``-elements, with *class*-attribute referring to an implementation of the interface: + +~~~ +public interface AdapterInstaller { + void install(ReadGraph g, AdaptionService service) throws Exception; +} +~~~ + +where + +~~~ +public interface AdaptionService { + void adapt(AsyncReadGraph g, + Resource r, Class clazz, boolean possible, + AsyncProcedure procedure); + + void adaptNew(AsyncReadGraph g, + Resource r, Class clazz, boolean possible, + AsyncProcedure procedure); + + void declareAdapter(Resource type, Class clazz); + + void addAdapter(Resource type, Class clazz, Adapter adapter); + + void addInstanceAdapter(Resource resource, Class clazz, Adapter adapter); +} +~~~ + +The class installs adapters by using method `addAdapter` (and `addInstanceAdapter`). +You should use the xml-file to define your adapters if possible. The installer-mechanism is provided for cases where you want to install an adapter that is constructed with special parameters. + +### Direct Adapters + +Direct adapters are used, when the user has a Resource and wants a specific Java interface e.g. for 2D diagrams, the user has an instance of DIA.Element and obtains an instance of org.simantics.diagram.adapter.ElementFactory. The Java instance can operate on the given resource for data-driven implementation of the ElementFactory. + +### Related Adapters + +Related adapters are used, when the user has a relation Resource and a subject Resource and wishes to obtain a Java interface associated to the L0.HasRange of the predicate, configured by a single statement defined by subject and relation. The adapter implementations are bound to the object Resource of the defining statement. The adapter implementation obtains the defining statement as parameter for data-driven implementation. + +An example: + +~~~ +L0X.StringAdapter ` and `` elements. These arguments will be directly forwarded to a constructor matching this argument list in your adapter implementation class. + + +| Term | Description | +|--------|--------| +|``|The resource that is being adapted.| +|``|`ReadGraph` for reading the graph database during construction of the adapted class instance.| +|``|`Bundle` class instance which identifies the bundle in which this adapter is defined. Can also be used to specify the referenced bundle explicitly using its symbolic name, e.g. `bundle.id`.| +|`FOO`|Any string constant contained as text within the tag. Passed to the constructor as a `java.lang.String` instance.| +|``|Requires attribute uri which must specify a URI to a relation resource. If adaption is performed for resource S, this argument element looks for a single object O from the statement (S, R, O) and passes O as an argument to the adapter class. If no single object O exists, the adapter is given null.| +|``|Requires attribute uri which must specify a URI to a relation resource. If adaption is performed for resource S, this argument element looks for all objects O existing in the database (S, R, O) and the objects to the adapter as a `Collection`.| +|``|Requires attribute uri which must specify a URI to an ordered set resource. Looks for the specified URI in the database, assumes it is an ordered set resource and loads the whole ordered set into a Collection<Resource> instance. This collection is passed as an argument to the adapter class.| + +## See Also + +[XML schema for adapter definition XML files](TODO: link to adapters.xsd) diff --git a/docs/Developer/Database/ResourceSerialization.md b/docs/Developer/Database/ResourceSerialization.md new file mode 100644 index 000000000..9a72e5695 --- /dev/null +++ b/docs/Developer/Database/ResourceSerialization.md @@ -0,0 +1,23 @@ +Serializing a resource + +~~~ + long serializeResource(RequestProcessor processor, Resource resource) throws DatabaseException { + SerialisationSupport ss = processor.getService(SerialisationSupport.class); + return ss.getRandomAccessId(resource); + } + String serializeResourceToString(RequestProcessor processor, Resource resource) throws DatabaseException { + return String.valueOf(serializeResource(processor, resource)); + } +~~~ + +Deserializing a resource + +~~~ + Resource deserializeResource(ReadGraph graph, long randomAccessId) throws DatabaseException { + SerialisationSupport ss = session.getService(SerialisationSupport.class); + return rs.getResource(randomAccessId); + } + Resource deserializeResource(ReadGraph graph, String randomAccessId) throws DatabaseException, NumberFormatException { + return deserializeResource(graph, Long.parseLong(randomAccessId)); + } +~~~ diff --git a/docs/Developer/Database/SubgraphExtents.md b/docs/Developer/Database/SubgraphExtents.md new file mode 100644 index 000000000..6ae1b454a --- /dev/null +++ b/docs/Developer/Database/SubgraphExtents.md @@ -0,0 +1,45 @@ +## Overview + +A subgraph extent is a set of statements bound by a given set of root resources. Subgraph extents are needed in + +* Copy-paste +* Import and export +* Delete + +## Domain + +A subgraph extent domain is the set of resources for which all parent resources are included in the root set. The set of parents for resource *r* are found by + +* including all resources `par` for statements `(r, p types(ReadGraph graph) throws DatabaseException; + void traverse(ReadGraph graph, Resource resource, Collection domain, Callback callback) throws DatabaseException; + +} +~~~ + +## Extent determination procedure + +* Determine the domain set by walking `IsRelatedTo` from root set and the discovered domain. +* Determine the set of extent descriptions found from root set dependencies (ontologies). +* Divide the set of available extent descriptions into included and excluded sets. +* Determine the set of (potentially) included statements by applying included extent descriptions on the domain set +* Determine the set of excluded statements by applying excluded extent descriptions on the domain set +* Remove the set of excluded statements from the included statement set diff --git a/docs/Developer/Database/Undo.md b/docs/Developer/Database/Undo.md new file mode 100644 index 000000000..72febc0f7 --- /dev/null +++ b/docs/Developer/Database/Undo.md @@ -0,0 +1,72 @@ +This page documents a simple global non-contextual undo/redo mechanism. + +## Mechanism + +Database client keeps a global list of undoable/redoable operations. Operations are added to undo list during commit. Each commit creates a change set to server which defines the changes to resource values and statements. Change set also contains metadata for interpreting the change. The metadata format is defined by client and the server can not read or interpret it. Each operation has a unique change set identifier. Sequential operations are tagged as combined by giving them the same operation id. All operations with same id will be treated as single undoable/redoable operation. + +### Combining write requests + +Currently there is only one way to combine several database write requests into a single operation. + +Normally an operation is created out of each separate write transaction. This happens when write transactions are initiated by invoking **Session.async/asyncRequest**. To combine a separate write transaction into a single operation, the write request must be initiated from within the write transaction using any **WriteGraph.async/asyncRequest** interface methods. The database client will group all write transaction change sets initiated like this into one operation. + +## Undo and redo handling in the Simantics workbench + +If **org.eclipse.ui.edit.undo** and **org.eclipse.ui.edit.redo** commands are bound to handlers **Session{Undo,Redo}Handler** then the undo/redo mechanism is activated. The handlers undo/redo one operation from the global undo/redo lists with each button press. It is desirable that each developer who develops requests that modify the graph comment their changes as shown by the following example. + +~~~ + session.sync(new WriteRequest() { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + // Do your modifications. + Layer0 b = Layer0.getInstance(graph); + Resource s = graph.newResource(); + graph.claim(s, b.InstanceOf, b.Entity); + + // Add comment to change set. + CommentMetadata cm = graph.getMetadata(CommentMetadata.class); + cm.add("My comment."); + graph.addMetadata(cm); + } + }); +~~~ + +You can test your operations using the following procedure: + +* Do the operation. +* Check from ''Teamwork'' / ''Graph History'' view which change sets have been created and that they are commented properly. + +## Marking undo-points in database version history + +Should the user simply perform write requests as described in [[#Undo and redo handling in the Simantics workbench]], each write request would simply create a new version into the database version history but all the changes would simply pile up into a single undoable operation. + +The database API has two methods for telling the database that it's current persistent state should be regarded as an undo point and new undoable operation should be started. These are in interfaces org.simantics.db.session and org.simantics.db.WriteOnlyGraph: + +~~~ +public interface Session extends RequestProcessor { + ... + /** + * Marks the current database state or the beginning of the current ongoing + * write transaction as an undo point. Calling this method several times + * before or during the same write transaction has no effect. + */ + void markUndoPoint(); + ... +} + + +public interface WriteOnlyGraph extends ServiceLocator, MetadataI { + ... + /** + * Marks the beginning of the ongoing write transaction as an undo point. + * Calling this method several times during the same transaction has no + * effect. + */ + void markUndoPoint(); + ... +} +~~~ + +## Related Documents + +* TODO: consider: [[Undo and Redo]] - internals of the mechanism. 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 diff --git a/docs/Developer/Database/VirtualGraphs.md b/docs/Developer/Database/VirtualGraphs.md new file mode 100644 index 000000000..66c20ee85 --- /dev/null +++ b/docs/Developer/Database/VirtualGraphs.md @@ -0,0 +1,121 @@ +# Intro + +The Simantics database client allows for defining memory-based and disk-based graph fragments which are applied on top of the server-based semantic graph. + +A reading user perceives the combined model and the virtual graph information can be specifically queried if necessary. + +A modifying user needs to specify in which virtual graph the modifications are made. + +Virtual graphs are manipulated via the following interfaces + +* **org.simantics.db.service.VirtualGraphSupport**, for management +* **org.simantics.db.request.WriteTraits**, for identifying the graph to write into + +## Specification + +A write request has a single graph into which it writes. This is determined by *WriteTraits* and usually as a parameter to *WriteRequest*. If null is provided, the client applies modifications into the persistent graph. The following rules apply: + +* New resources are created into the given virtual graph +* Claim statements are added into the given virtual graph +* Value changes are applied into the given virtual graph +* When the virtual graph provided to the WriteRequest is: +** **null**: +*** For denied statements the location of the statement is determined and the statement is removed from that virtual graph. If the denied statement is not a part of any virtual graph, it is removed from the persistent graph. +** **non-null**: +*** Statements are only removed from the virtual graph specified for the write request + +The user can perform modifications into multiple virtual graphs within a single transaction. This is accomplished by issuing a new synchronous modification (WriteGraph.sync) into a new virtual graph. + +## Examples + +The following code examples show in practice how to write into virtual graphs, both memory- and disk-based. Writing to several virtual graphs within the same write transaction is also demonstrated. + +~~~ +package org.simantics.db.tests.api.support.virtualGraphSupport; + +import org.simantics.db.ReadGraph; +import org.simantics.db.RequestProcessor; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.Statement; +import org.simantics.db.VirtualGraph; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.common.request.WriteResultRequest; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.service.VirtualGraphSupport; +import org.simantics.layer0.Layer0; + +public class VirtualGraphExample { + + public Resource createLibrary(RequestProcessor processor, VirtualGraph vg, final String libraryName) + throws DatabaseException { + return processor.syncRequest(new WriteResultRequest(vg) { + @Override + public Resource perform(WriteGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Resource r = graph.newResource(); + graph.claim(r, L0.InstanceOf, null, L0.Library); + graph.claimLiteral(r, L0.HasName, libraryName); + return r; + } + }); + } + + public void testVirtualGraphs(Session session) throws DatabaseException { + VirtualGraphSupport vgSupport = session.getService(VirtualGraphSupport.class); + VirtualGraph memory = vgSupport.getMemoryPersistent("memory"); + VirtualGraph workspace = vgSupport.getWorkspacePersistent("workspace"); + // NOTICE: resource are created in difference virtual graphs in separate + // transactions through Session.syncRequest. + Resource memResource = createLibrary(session, memory, "memory"); + Resource workspaceResource = createLibrary(session, workspace, "workspace"); + printVirtualGraphs(session); + } + + public void testMultipleVirtualGraphsInSameTransaction(Session session) throws DatabaseException { + final VirtualGraphSupport vgSupport = session.getService(VirtualGraphSupport.class); + session.syncRequest(new WriteRequest() { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + VirtualGraph memory = vgSupport.getMemoryPersistent("memory"); + VirtualGraph workspace = vgSupport.getWorkspacePersistent("workspace"); + // NOTICE: resource are created in different virtual graphs in + // the same transaction through WriteGraph.syncRequest + Resource memResource = createLibrary(graph, memory, "memory"); + Resource workspaceResource = createLibrary(graph, workspace, "workspace"); + } + }); + printVirtualGraphs(session); + } + + public void printVirtualGraphs(Session session) throws DatabaseException { + session.syncRequest(new ReadRequest() { + @Override + public void run(ReadGraph graph) throws DatabaseException { + VirtualGraphSupport vgSupport = graph.getService(VirtualGraphSupport.class); + for (VirtualGraph vg : vgSupport.listGraphs()) { + for (Statement stm : vgSupport.listStatements(vg)) { + System.out.println("Statement: " + NameUtils.toString(graph, stm)); + } + for (Resource r : vgSupport.listValues(vg)) { + System.out.println("Literal value: " + graph.getValue(r)); + } + } + } + }); + } + +} +~~~ + +## Debugging + +The standard Simantics Graph Debugger view shows for every statement which virtual graph it belongs to. This information is visible on the *Graph* column of the statement table. The Graph column will show: +;DB: when the statement is in the persistent graph +;'name' (W): when the statement is in a named workspace-persistent virtual graph +;'name' (M): when the statement is in a named memory-persistent (transient) virtual graph + +[[Image:GraphDebuggerVG.png|frame|center|Graph debugger example with statements in virtual graphs.]] diff --git a/docs/Developer/Introduction.md b/docs/Developer/Introduction.md new file mode 100644 index 000000000..705d74e97 --- /dev/null +++ b/docs/Developer/Introduction.md @@ -0,0 +1,11 @@ +# Simantics Developer Documentation + +Simantics is a software platform for modelling and simulation. The system has client-server architecture with a semantic ontology-based modelling database and Eclipse framework -based client software with plug-in interface. The Simantics platform and many of its components are open source under Eclipse Public License (EPL). + +The philosophy of the Simantics platform is to offer an open, high level application platform on which different computational tools can be easily integrated to form a common environment for modelling and simulation. The platform includes several modelling tools, so-called editors, for e.g. 2D graph-like hierarchical model composition and semantic graph browsing. + +One of the biggest innovations in the Simantics platform is the semantic modelling approach itself and high-level ontology tools. The semantic database, i.e. triplestore, on the server side enables high performance data management and arbitrary mappings of data. This enables e.g. efficient mapping of simulation and measurement data to the model configuration and its visualisation. + +The Simantics development and maintenance process is built to be solid and scalable from the very beginning. The objective is to aim far to the future what comes to requirements for scalability, usability, and reliability. + +This Simantics Developer Documentation is targeted to programmers and software developers developing either the platform itself or additional plug-ins to be used with or on the platform. The Simantics End User Documentation complements to documentation for the Simantics platform offering overview and detailed information about the platform from the user's point of view. The [Simantics website](https://www.simantics.org/) is the source of information for the Simantics project and related subjects. diff --git a/docs/Developer/Ontology/BinaryContainerFormat.md b/docs/Developer/Ontology/BinaryContainerFormat.md new file mode 100644 index 000000000..f7bcfc3a5 --- /dev/null +++ b/docs/Developer/Ontology/BinaryContainerFormat.md @@ -0,0 +1,14 @@ +Simantics binary container format is a format for storing binary encoded data to file system. + +~~~ +type DataContainer = { + format : String, + version : Integer, + metadata : Map(String, Variant), + content : Variant +} +~~~ + +In addition to the actual content data that is stored as a Variant, a file contains a format name that tells how the data is used: for example "aprosModel", "aprosSymbol" and a version number. Applications use format name to decide if the file can be used for the operation the user has requested and version number to choose how to handle the data. + +Version number is increased by one every time the data type of the content or the semantics of the data (how it should be handled in the application) is changed. diff --git a/docs/Developer/Ontology/GraphCompiler.md b/docs/Developer/Ontology/GraphCompiler.md new file mode 100644 index 000000000..c2d48615c --- /dev/null +++ b/docs/Developer/Ontology/GraphCompiler.md @@ -0,0 +1,11 @@ +# Introduction + +The *org.simantics.graph* feature provides a textual graph database source editing environment that integrates into the Eclipse IDE. + +The feature adds a new project nature and builder for Java/Plug-in projects to support development of `.pgraph` *partial graph* source files. The environment will compile all partial graphs with in a project into a single *[Transferable Graph](TransferableGraph.md)* `/graph.tg`. Simantics applications will then be able to integrate this transferable graph into handled databases. + +The environment also allows you to add the graph compilation nature to any project from the project's context menu *Configure/Add graph compilation*. + +# Install + +The graph development environment is distributed using an update site at [http://www.simantics.org/update/utils](http://www.simantics.org/update/utils). diff --git a/docs/Developer/Ontology/GraphFileFormat.md b/docs/Developer/Ontology/GraphFileFormat.md new file mode 100644 index 000000000..6ea38fe72 --- /dev/null +++ b/docs/Developer/Ontology/GraphFileFormat.md @@ -0,0 +1,219 @@ +Textual graph format is used to write ontologies in Simantics platform. [Graph Compiler](GraphCompiler.md) translates textual format to binary [Transferable graphs](TransferableGraph.md) that can be then imported to Simantics database. + +## Syntax + +### Indentation + +The graph format is indentation based. The amount of indentation can be freely chosen, but it has to be consistent with the previous lines. The lines can be indendented only with spaces. Tabulators are not allowed. + +Both C and C++ style comments are allowed. If a C style comment begins a line, there must not be anything else in the line after the comment, otherwise the amount of indentation would be then unclear. Text in parenthesis (), [] and {} can be freely indented. + +### Statements + +A graph file describes a collection of statements. One statement is written as: + + Subject Predicate Object + +and represented graphically as + +![Subject Predicate Object](Images/SubPreObj.png) + +Statements with the same subject can be written in the same line + + Subject Predicate1 Object1 Predicate2 Object2 Predicate3 Object3 + +or with multiple indented lines + +~~~ + Subject + Predicate1 Object1 + Predicate2 Object2 + Predicate3 Object3 +~~~ + +These forms can also be combined + +~~~ + Subject Predicate1 Object1 Predicate2 Object2 + Predicate3 Object3 + Predicate4 Object4 +~~~ + +Statements with the same subject and predicate can be abbreviated as + +~~~ + Subject + Predicate + Object1 + Object2 + Object3 +~~~ + +In this case, there must be no objects in the same line with the predicate. + +Statements concerning the object of another statement can be written after the object if the object is not in the same line with the original subject. The following texts describe the same graph: + +![Subject Predicate Object Chain](Images/SubPreObj_chain_01.png) + +~~~ +Subject Predicate1 Object1 +Object1 Predicate2 Object2 + +Subject + Predicate1 Object1 Predicate2 Object2 + +Subject + Predicate1 Object1 + Predicate2 Object2 + +Subject + Predicate1 + Object1 Predicate2 Object2 + +Subject + Predicate1 + Object1 + Predicate2 Object2 + +Subject + Predicate1 + Object1 + Predicate2 + Object2 +~~~ + +### Resources + +Subject, predicate, and object of a statement are all resources. Predicates and other resources are handled little differently. A resource can be in one of the following forms: +* Identifier +* URI +* Reference to a child of another resource +* Literal value (not allowed as a predicate) +* Data type (not allowed as a predicate) +* A special relation: =, -characters and must begin with "http:". For example +``. + +The syntax of literal values and data types is specified in [[Databoard Specification#Value Text Notation]]. Variants and tagged values (of some union type) must be enclosed in parenthesis. A data type must be prefixed with $ and union types and array types must be enclosed in parenthesis. + +References are used to refer URIs: If identifier A referes to , then A.Foo referes to . If URI contains non-alphanumerical characters, the name can be enclosed in double quotes. For example A."Cul-de-sac" refers to . + +Special relations are abbreviations of some Layer0 relations: +{| +|- +|width="40px" align="center"| = +| http://www.simantics.org/Layer0-1.0/Equals +|- +|width="40px" align="center"| http://www.simantics.org/Layer0-1.0/Inherits +|- +|width="40px" align="center"| http://www.simantics.org/Layer0-1.0/SubrelationOf +|- +|width="40px" align="center"| : +| http://www.simantics.org/Layer0-1.0/InstanceOf +|- +|width="40px" align="center"| >-- +| http://www.simantics.org/Layer0-1.0/IsDomainOf +|- +|width="40px" align="center"| <-- +| http://www.simantics.org/Layer0-1.0/HasDomain +|- +|width="40px" align="center"| --> +| http://www.simantics.org/Layer0-1.0/HasRange +|- +|width="40px" align="center"| ==> +| http://www.simantics.org/Layer0-1.0/RequiresValueType +|} + +### Templates + +A template is a parametrized graph that can be defined and instantiated. + +A template is defines as follows: + +~~~ + : L0.Template + @template +