From a36bb585231a07dfdb1a67201a5825ea89c8dbcc Mon Sep 17 00:00:00 2001 From: niemisto Date: Wed, 18 Nov 2009 13:41:39 +0000 Subject: [PATCH] git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@13135 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.objmap/.classpath | 8 + org.simantics.objmap/.hgignore | 5 + org.simantics.objmap/.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 8 + org.simantics.objmap/META-INF/MANIFEST.MF | 18 + org.simantics.objmap/build.properties | 7 + .../doc/bidirectionalModel.graphml | 108 +++++ .../doc/bidirectionalModel.png | Bin 0 -> 5101 bytes org.simantics.objmap/doc/main.mediawiki | 26 ++ org.simantics.objmap/doc/manual.mediawiki | 167 +++++++ .../doc/objectMappingTerminology.graphml | 202 +++++++++ .../doc/objectMappingTerminology.png | Bin 0 -> 3645 bytes .../doc/triangleModel.graphml | 89 ++++ org.simantics.objmap/doc/triangleModel.png | Bin 0 -> 4545 bytes .../objmap/examples/SysdynExample.java | 51 +++ .../src/org/simantics/objmap/IFunction.java | 13 + .../src/org/simantics/objmap/ILinkType.java | 21 + .../src/org/simantics/objmap/IMapping.java | 127 ++++++ .../simantics/objmap/IMappingListener.java | 19 + .../org/simantics/objmap/IMappingRule.java | 33 ++ .../org/simantics/objmap/IMappingSchema.java | 20 + .../simantics/objmap/MappingException.java | 29 ++ .../src/org/simantics/objmap/Mappings.java | 38 ++ .../objmap/annotations/Composition.java | 11 + .../objmap/annotations/GraphType.java | 16 + .../objmap/annotations/RelatedElement.java | 16 + .../objmap/annotations/RelatedElements.java | 16 + .../objmap/annotations/RelatedValue.java | 21 + .../annotations/factories/DataTypeUtils.java | 46 ++ .../factories/RelatedElementRuleFactory.java | 28 ++ .../factories/RelatedElementsRuleFactory.java | 29 ++ .../factories/RelatedValueRuleFactory.java | 33 ++ .../annotations/meta/HasClassRuleFactory.java | 14 + .../annotations/meta/HasFieldRuleFactory.java | 14 + .../meta/HasMethodRuleFactory.java | 14 + .../src/org/simantics/objmap/impl/Link.java | 29 ++ .../org/simantics/objmap/impl/Mapping.java | 411 ++++++++++++++++++ .../objmap/impl/RangeUpdateRequest.java | 64 +++ .../objmap/impl/UnidirectionalMapping.java | 254 +++++++++++ .../objmap/rules/MappedElementRule.java | 55 +++ .../objmap/rules/MappedElementsRule.java | 60 +++ .../org/simantics/objmap/rules/ValueRule.java | 52 +++ .../objmap/rules/domain/IDomainAccessor.java | 15 + .../objmap/rules/domain/MappingUtils.java | 74 ++++ .../rules/domain/RelatedObjectAccessor.java | 60 +++ .../rules/domain/RelatedObjectsAccessor.java | 48 ++ .../rules/domain/RelatedValueAccessor.java | 72 +++ .../rules/factory/IClassRuleFactory.java | 11 + .../rules/factory/IFieldRuleFactory.java | 12 + .../rules/factory/IMethodRuleFactory.java | 12 + .../objmap/rules/range/FieldAccessor.java | 63 +++ .../objmap/rules/range/IRangeAccessor.java | 12 + .../objmap/schema/MappingSchemas.java | 83 ++++ .../objmap/schema/SimpleLinkType.java | 121 ++++++ .../simantics/objmap/schema/SimpleSchema.java | 55 +++ 55 files changed, 2838 insertions(+) create mode 100644 org.simantics.objmap/.classpath create mode 100644 org.simantics.objmap/.hgignore create mode 100644 org.simantics.objmap/.project create mode 100644 org.simantics.objmap/.settings/org.eclipse.jdt.core.prefs create mode 100644 org.simantics.objmap/META-INF/MANIFEST.MF create mode 100644 org.simantics.objmap/build.properties create mode 100644 org.simantics.objmap/doc/bidirectionalModel.graphml create mode 100644 org.simantics.objmap/doc/bidirectionalModel.png create mode 100644 org.simantics.objmap/doc/main.mediawiki create mode 100644 org.simantics.objmap/doc/manual.mediawiki create mode 100644 org.simantics.objmap/doc/objectMappingTerminology.graphml create mode 100644 org.simantics.objmap/doc/objectMappingTerminology.png create mode 100644 org.simantics.objmap/doc/triangleModel.graphml create mode 100644 org.simantics.objmap/doc/triangleModel.png create mode 100644 org.simantics.objmap/examples/org/simantics/objmap/examples/SysdynExample.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/IFunction.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/ILinkType.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/IMapping.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/IMappingListener.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/IMappingRule.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/IMappingSchema.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/MappingException.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/Mappings.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/Composition.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/GraphType.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElement.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElements.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedValue.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/factories/DataTypeUtils.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementsRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedValueRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasClassRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasFieldRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasMethodRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/impl/Link.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/impl/Mapping.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/impl/RangeUpdateRequest.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/impl/UnidirectionalMapping.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementRule.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementsRule.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/ValueRule.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/domain/IDomainAccessor.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/domain/MappingUtils.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectAccessor.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectsAccessor.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedValueAccessor.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/factory/IClassRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/factory/IFieldRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/factory/IMethodRuleFactory.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/range/FieldAccessor.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/rules/range/IRangeAccessor.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/schema/MappingSchemas.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/schema/SimpleLinkType.java create mode 100644 org.simantics.objmap/src/org/simantics/objmap/schema/SimpleSchema.java diff --git a/org.simantics.objmap/.classpath b/org.simantics.objmap/.classpath new file mode 100644 index 00000000..23e107f9 --- /dev/null +++ b/org.simantics.objmap/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/org.simantics.objmap/.hgignore b/org.simantics.objmap/.hgignore new file mode 100644 index 00000000..73df90f6 --- /dev/null +++ b/org.simantics.objmap/.hgignore @@ -0,0 +1,5 @@ +syntax: regexp +^bin/ + +syntax: glob +*.svn/* \ No newline at end of file diff --git a/org.simantics.objmap/.project b/org.simantics.objmap/.project new file mode 100644 index 00000000..82c191c2 --- /dev/null +++ b/org.simantics.objmap/.project @@ -0,0 +1,28 @@ + + + org.simantics.objmap + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.simantics.objmap/.settings/org.eclipse.jdt.core.prefs b/org.simantics.objmap/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..fc058b8d --- /dev/null +++ b/org.simantics.objmap/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Wed Nov 11 10:38:27 EET 2009 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/org.simantics.objmap/META-INF/MANIFEST.MF b/org.simantics.objmap/META-INF/MANIFEST.MF new file mode 100644 index 00000000..3085d0a1 --- /dev/null +++ b/org.simantics.objmap/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Objmap +Bundle-SymbolicName: org.simantics.objmap +Bundle-Version: 0.1.0 +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Require-Bundle: org.simantics.db;bundle-version="0.6.2", + gnu.trove2;bundle-version="2.0.4", + org.simantics.layer0.utils;bundle-version="0.6.2", + org.apache.log4j;bundle-version="1.2.15" +Export-Package: org.simantics.objmap, + org.simantics.objmap.annotations, + org.simantics.objmap.annotations.meta, + org.simantics.objmap.rules, + org.simantics.objmap.rules.domain, + org.simantics.objmap.rules.factory, + org.simantics.objmap.rules.range, + org.simantics.objmap.schema diff --git a/org.simantics.objmap/build.properties b/org.simantics.objmap/build.properties new file mode 100644 index 00000000..98921497 --- /dev/null +++ b/org.simantics.objmap/build.properties @@ -0,0 +1,7 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . +src.includes = doc/,\ + META-INF/,\ + examples/ diff --git a/org.simantics.objmap/doc/bidirectionalModel.graphml b/org.simantics.objmap/doc/bidirectionalModel.graphml new file mode 100644 index 00000000..2a52aa11 --- /dev/null +++ b/org.simantics.objmap/doc/bidirectionalModel.graphml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + Intermediate model + + + + + + + + + + + + Database + + + + + + + + + + + + Editor + + + + + + + + + + + + + reads + + + + + + + + + + + + + visualizes + + + + + + + + + + + + + modifies + + + + + + + + + + + + + writes + + + + + + + + + diff --git a/org.simantics.objmap/doc/bidirectionalModel.png b/org.simantics.objmap/doc/bidirectionalModel.png new file mode 100644 index 0000000000000000000000000000000000000000..77bcc302bf1e551dad8ab7808a409ea659e905db GIT binary patch literal 5101 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 P1fx(_(NRV}gogbe5_org.simantics.objmap-plugin tries to make the implementation of bidirectional model easier by providing a framework for defining declaratively the update procedure between database and intermediate model. It can also be used with triangle model only for one direction or with hybrid model where some operations are implemented using the intermediate model and other modifying the database directly. + += Design principles = + +; Symmetric +: For every operation from database to Java objects there is a corresponding operation from Java objects to database. This makes the framework easier to learn and undestand. +; Non-intrusive +: The Java objects used with the framework do not need to implement any specific interface or follow some specific naming convention. The mapping schema can be defined using annotations or separately from the class definition. +; Support for different use scenarios +:* bidirectional / unidirectional +:* one shot / continuous +:* automatic listening / manual updating +; One-to-one +: For every resource there is a single corresponding Java object and vise versa. This makes the framework easier to understand. It is not a transformation framework. + += Concepts = + +''Mapping'' consists of a set of resources called a ''domain'', a set of Java objects called a ''range'' and a collection of ''links''. Each link is attached to exactly one domain and range element and each domain and range element has at most one link attached to it. Additionally the link has a ''link type'' that contains requirements for the domain and range elements in the same link. + +[[Image:objectMappingTerminology.png]] + +A mapping is ''up-to-date'' if every domain and range element has a link and all links satisfy the requirements of their link types. The links of up-to-date mapping form a bijection from domain to range. + +A ''mapping schema'' associates all domain and range elements with a link type. It is used to add new domain and range elements to the mapping. + += Mapping interface = + +The plugin represents a mapping with interface org.simantics.objmap.IMapping. The interface is symmetric in the sense that every operation on the domain of the mapping has also a counterpart that operates on the range. Typically, if one of the operations requires a read graph, its counterpart requires a write graph. We will describe only the methods operating on the domain of the mapping: + + Set getDomain(); + +Returns the domain of the mapping. All set operations are supported. Adding a new domain element does not automatically create a link to it. Removal of a domain element removes also a link and the target element, but does not remove the element from the database. + + Collection updateDomain(WriteGraph g) throws MappingException; + +Updates all domain elements whose counterpart is modified and creates new domain elements for previously added range elements. Returns the collection of domain elements that were modified or created in the update process. + + Object get(Resource domainElement); + +Returns the counterpart of a domain element or null if the element does not belong to the domain or does not have a link. + + Object map(ReadGraph g, Resource domainElement) throws MappingException; + +A convenience method that adds a domain element to the mapping and immediately updates the mapping and returns the corresponding range element. + + void domainModified(Resource domainElement); + +Tells the mapping that the domain element has been modified. + + boolean isDomainModified(); + +Tells if some domain elements have been modified or added. + + Collection getConflictingDomainElements(); + +Returns a collection of domain elements which have been modified and also their counterparts in the mapping are modified. These elements are in conflict in the sense that the updating domain and range in different orders may produce different results. + + void addMappingListener(IMappingListener listener); + void removeMappingListener(IMappingListener listener); + +Adds or removes a listener for domain and range modifications. + += Defining a mapping schema = + +The primary way for defining a mapping schema is to use Java annotations. The current annotation support is still lacking. Only the following annotations are supported: +; GraphType(uri) +: Specifies the domain type that the class corresponds to. +; RelatedValue(uri) +: Specifies a correspondence between a field and functional property. +; RelatedElement(uri) +: Specifies a correspondence between a field and functional relation +; RelatedElements(uri) +: Specifies a correspondence between a field and a relation. The type of the field has to be a collection. + +== Example == + +Suppose we have the following annotated classes: + @GraphType("http://www.simantics.org/Sysdyn#Configuration") + static class Configuration { + @RelatedElements("http://www.vtt.fi/Simantics/Layer0/1.0/Relations#ConsistsOf") + Collection components; + } + + static abstract class Component { + } + + @GraphType("http://www.simantics.org/Sysdyn#Dependency") + static class Dependency extends Component { + @RelatedElement("http://www.simantics.org/Sysdyn#HasTail") + Variable tail; + @RelatedElement("http://www.simantics.org/Sysdyn#HasHead") + Auxiliary head; + } + + static abstract class Variable extends Component { + @RelatedValue("http://www.vtt.fi/Simantics/Layer0/1.0/Relations#HasName") + String name; + } + + @GraphType("http://www.simantics.org/Sysdyn#Auxiliary") + static class Auxiliary extends Variable { + } + +Them the schema can be defined as follows: + SimpleSchema schema = new SimpleSchema(); + schema.addLinkType(MappingSchemas.fromAnnotations(g, Configuration.class)); + schema.addLinkType(MappingSchemas.fromAnnotations(g, Dependency.class)); + schema.addLinkType(MappingSchemas.fromAnnotations(g, Auxiliary.class)); + += Using the mapping interface = + +Assume that a mapping scheme scheme has already been defined and modelRoot is the root resource of the model that the editor edits. Then the model is created as follows: + IMapping mapping = Mappings.create(scheme); + in read transaction { + MyModel model = (MyModel)mapping.map(graph, modelRoot); + } + +There are different ways how the mapping can be updated. The following code forces update for all domain elements. + in read transaction { + for(Resource r : mapping.getDomain()) + mapping.domainModified(r); + mapping.updateRange(graph); + } + +If the range elements have some kind of "dirty" flags, the update can be optimized: + in write transaction { + for(Object obj : mapping.getRange()) + if(obj implements MyObject && ((MyObject)obj).isDirty()) + mapping.rangeModified(obj); + mapping.updateDomain(graph); + } + +Often the editor has to update some auxiliary structures when the mapping modifies the range. This can be implemented for example as: + for(Object obj : mapping.updateRange(graph)) + if(obj implements MyObject) + ((MyObject)obj).updateAuxiliary(); + +The most convenient way for updating the target would be to add graph request listeners for each domain element in the mapping. This is not yet implemented although the current interface should support this without modifications. Currently the only way to listen the database changes is to listen the request that is used to call the updateRange-method. + += Development plan = + +By priority: +* Automatic listening of database changes: marks domain elements modified. +* More complete annotations +* Utilizing declarations in ontologies: for example full URIs of relations are not needed because relations are declared in types. Also the validity of annotation can be checked. +* A separate plugin containing only mapping annotations so that the plugin can be used without introducing a dependency to org.simantics.db. +* Composition annotation that can be used to remove elements whose parents are removed or update elements whose parents are marked modified. +* Support for copy-paste and other extra mapping issues. diff --git a/org.simantics.objmap/doc/objectMappingTerminology.graphml b/org.simantics.objmap/doc/objectMappingTerminology.graphml new file mode 100644 index 00000000..c3cc6b27 --- /dev/null +++ b/org.simantics.objmap/doc/objectMappingTerminology.graphml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + mapping + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + domain element + + + + + + + + + + + link + + + + + + + + + + + range element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + link type + + + + + + + + + + + + + + + + + + + diff --git a/org.simantics.objmap/doc/objectMappingTerminology.png b/org.simantics.objmap/doc/objectMappingTerminology.png new file mode 100644 index 0000000000000000000000000000000000000000..e9ee1fba6696e2e28fc689da86ab1d455eee75a0 GIT binary patch literal 3645 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 literal 0 HcmV?d00001 diff --git a/org.simantics.objmap/doc/triangleModel.graphml b/org.simantics.objmap/doc/triangleModel.graphml new file mode 100644 index 00000000..9ab3b29f --- /dev/null +++ b/org.simantics.objmap/doc/triangleModel.graphml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + Intermediate model + + + + + + + + + + + + Database + + + + + + + + + + + + Editor + + + + + + + + + + + reads + + + + + + + + + + + visualizes + + + + + + + + + + + modifies + + + + + + + + + diff --git a/org.simantics.objmap/doc/triangleModel.png b/org.simantics.objmap/doc/triangleModel.png new file mode 100644 index 0000000000000000000000000000000000000000..0918ef8a549c8af966d7e501f5473143164be2b9 GIT binary patch literal 4545 zcma)Ac|4Te7a!hGC=!MeW6zSwuE@R($yO7>)V!9EB}-C_5C&n4WQpdrCm}mo#?pwv zTV%#d11%a}cbvs6$aWI$A)VQJMl7 zh2*3-K@n^!Ymvjf+Es&bvbBR5s|&U zgYAp$_Z8A5iehM+Gy&qWQ)Xk(sydF=+|~0c6VCwR(mQ1a0#TVjYiuA8iUCAtJQP6q z|AeQA|AZDO=t$6h=Xr96^)woxFb-8>j88;7GWH(rkYH^ zC^jT>92U9Gh6=l(VV{5L$`v~}{3Q;Duc+9l{doc=h0DrPR#ojbijR-C8u|qb3aXcW z(lpuD);2RULm(L7zk64XRGAr!wsm#E^z9ULY)JB>RF*lQtdgS<|#I)t>eH2l`B{P*wQLkiFZ zCcFCzWuLoKp^}o4`4F;i!-NDSrfQYSgr7>~ql*~*oo6ZxFTS~#+~04tV!h&C`~nJv znyEO~F~P14@XE=_^_1cDZN>L@$&{o>M0`w@pRPIHw@rCe~OK8|Lk%t*WD1p;S^FJtu4W?<-MS4NNeW z8!p22hSb!dp&?>j$Nc8X>Z*}#e%ey56;F}2j!qaXM6=d8xy=JvZu2lO(3zftKZ|tCUs(P+G9pE0tv^88;6DF6 zT3%cbukmF2M{5L&`s={~O3@f6#y1LdfWG`Oq>NM_;E-x>? zP|O_?GGK17F)x>Dy0_o?UK2NQw0*WiTpfRKR%ne)_>__+O88?~$0w*Z`YZJ*;`oDq z*4KXzGw5b^u+VkUfBgaNI-8in$7w-fM&Jdrva-_Js`IK>g5}kxS=d5gNlA%q$q+8| zAl>B?6w{RnfbYr9za(E0iBzOm&wW7O{H!194dK+5r!pziHQK-pjAN>zp<;~5nt1<)x1!IwujX}9)=o3_?e!;HuVh`@Qy8~$H z@6InMkmyprtf~1Dy0Wlvq&Yr4oj;C{LvuzVMQ8zPT3TR2XB|6X3fw8c3kH|Ki`5Jq z#;Zr}?*u$UI6Br;RiTkpwZ}oIu&tMI>g&5me`U_Z9?~{Q?38CntaU)?2Ux z-+@Dpv9GEX-QL+TW%UaV4qoN)YnZrs^CpZMCa}gPc8s61>%FnF+$dMqd8@p~5fKp= znDN?Cod%eQAWzSW0n&@>kM!k>Fy;)XFuplt<%sSaHseu~W5a|XJM*G4ozYD+RU7j3 zBm=(1VPkvc_GS22x$|qZ;6p7Yv+IxgW&HYPclIm+Tet!HI;VZ|D3Q-oQc?~hVA#I} zijhu_>vZ<^hE4=Jd3u_0SJu@T<7U9dDLvxJUSfBNe00VlT?7I_p2)AMth9#1qmXNC z$Bs2m~aoQ+}wwAA8fc zFD}Kl=*Um!t7>XoZj-qftvjZ`#+{(-q9Q2|MetSrhl;|euqTGSF%x}#VfNgPWwiwb zQHPVpb;wo-#Al8w;cM5feOuYi$~vFHh}X_UTkFet)t;+=mMd5@`R&_3Qfq7gw9t+e@R-4s$qM=DwE~kS@F1uvjeMzUsBPxxDP`R%H1#b8`>j=Eg?ZViK7Qhr?03 zTT-Iu;8(o>93wNdw#K~iR}b@0Utf|^!X#T%d_ip#T;&HELl>XN>o0xnS&*XpY8{Jc z1z?5y3?`lMad9DUZ^MZb6BE85&F{p>{kr3$ zfYWZ?6=}x!E z+LePs^{&77mF>DP)t75D@`Zfn%$b}tAa#B&m2#vm7UHg`tDmgIVhq1(HZ?a75u?tZ zcJyhOkTy{GZ8vtH#~^`LU0t1~4g)VJz;C4r&y&eZOG^W~afFJ72AQx@{D6pafX>}J zceF#p?gRzVRznNazDTO2#}6;_lz4l3rk8DZL-EFXZqODLZzZYDP!ctCf491(0N4*V zYM(NUG16w=Q&N1)iWfUU+1RF%&wSZ4=qGn{7@nvj5=}f4^7He>PZ(W3nGhfECR2gkYKn{1F zrq^$Q|1LiL+N}lJg<(^)EIhkJ?&#~Y1TsM0`g^p8oK*MZDte@x02&7#$l6(vv{JIO>l@ z=wyh*b5>RX9qWN$x?z z#Q24zo3T>T(y`t%IxSH7ZUV{oL9Qzt;Co9uJ0e@#x+&x7!m8TZ!htv=VYg$94Ez_e zft_#;$Qa$Wv%{DURU9RM6(uEo>#0=rf9*#{M<2xsg||XtTcLrnVE!`!0Rh5bi`WNh z0zD>?h@T+k%jnL?0?BvO~Vvb=nKh7gw-I(6O#_*@SVMR~b=24nNHk&%&{oSd@eT@xCB zW+pr&^;dVK(eRY%apnjvT~AL>MCg7%LV|3lkx$K@^OGm%>RVEMf`i9LM>EcZB_y1p zWAgz=k||Hs?|OJF_r6kv7b|~+1_cF)Kbj)hcte0La&0cof%nZm z0ek|G7Xen0O2=C$1(b0m5g;iBbKt0`E2q6mM_5E_2YAj^ZK53w09!7|O;|R!7 zzD?G=NW{?aFhKZK^E*Mebl^1cgaE6HZ@&&gP2{nJHyglm0Rh$Y+Ppj`cXyMr(021d z)+bM%%exba+(27pWhF4l79jEqq=t!4o12@!@kO_1 zYlQ$+&U&~dU8G$4w2b8N;^HEPZXmQ-kr6@gr%jo z+`PQJtZZz#bf6Qt-BFDHb+89?6J|p%u3MGTsOz~ysvEUNMn+aPc6LiED^2YoKbDqQ zaU<96Bpj#bFnYVV7|y_R&G6o@*H|Rb*JiP1Yj)j6; z(TI=S?d|QpP9}_8lS5?OTXuqzne7{^-th2nfD0%075qPpg=lDMZa>mcfUxolIj{7% z%t5Ei-QVzRu3bMEOlPk@u)2L)T|r@HlLi!~?XT4O3&+OBLVs;0zkd0gue1m=IEfGa zwa_O@L9AqA>Ox-lyP;5y0|io1%MDZmtaFM{cUOnL3>QxNW>N~N5sg5OO>Jyc5Rgfs zgQ5A20L2vjgR+p`wv-HRH_!j_b^Q>s{KSm=u6};j@MIlKaFL4=zml?w3K2^r64e_e z33+{DUX704p`lvQk6%&=YrAo~(^eH}Kqusbzb9XYaslu`zx8YHH0m%Gx;hNop~vJM z{-Q=gu9r{ru`0B@ICikNiZ^e#D*hVio?(=U7;>xp2WI>ip>&~-R{Ukdw{2`}?Cl%t z>K1nPJXKyfaz?3U8wLIKq?4%|BYBY>*Yyt&dizP1s36MHU~g}HT7GG%my^>RP%`}z zZJIV)u$*iu< ze6q!hnin~($PYwywpK+a^!LIIDA6Qo&W&r#4iSijMA?lv;J;+m_vai#PlW!bh3@(l zGm{Jo&PK3%>#E|zCe{|W*N%*>+ZlTLtV8jD`ROzMObO8caeV;-O@Tpw?lOQokKe-K eoyX6lUkvg(6tm#!I%D8!1_U#>u3xHqC;Wf=XcNBx literal 0 HcmV?d00001 diff --git a/org.simantics.objmap/examples/org/simantics/objmap/examples/SysdynExample.java b/org.simantics.objmap/examples/org/simantics/objmap/examples/SysdynExample.java new file mode 100644 index 00000000..57092cb7 --- /dev/null +++ b/org.simantics.objmap/examples/org/simantics/objmap/examples/SysdynExample.java @@ -0,0 +1,51 @@ +package org.simantics.objmap.examples; + +import java.util.Collection; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.IMappingSchema; +import org.simantics.objmap.annotations.GraphType; +import org.simantics.objmap.annotations.RelatedElement; +import org.simantics.objmap.annotations.RelatedElements; +import org.simantics.objmap.annotations.RelatedValue; +import org.simantics.objmap.schema.MappingSchemas; +import org.simantics.objmap.schema.SimpleSchema; + +public class SysdynExample { + + @GraphType("http://www.simantics.org/Sysdyn#Configuration") + static class Configuration { + @RelatedElements("http://www.vtt.fi/Simantics/Layer0/1.0/Relations#ConsistsOf") + Collection components; + } + + static abstract class Component { + } + + @GraphType("http://www.simantics.org/Sysdyn#Dependency") + static class Dependency extends Component { + @RelatedElement("http://www.simantics.org/Sysdyn#HasTail") + Variable tail; + @RelatedElement("http://www.simantics.org/Sysdyn#HasHead") + Auxiliary head; + } + + static abstract class Variable extends Component { + @RelatedValue("http://www.vtt.fi/Simantics/Layer0/1.0/Relations#HasName") + String name; + } + + @GraphType("http://www.simantics.org/Sysdyn#Auxiliary") + static class Auxiliary extends Variable { + } + + public static IMappingSchema createSchema(ReadGraph g) throws DatabaseException, InstantiationException, IllegalAccessException { + SimpleSchema schema = new SimpleSchema(); + schema.addLinkType(MappingSchemas.fromAnnotations(g, Configuration.class)); + schema.addLinkType(MappingSchemas.fromAnnotations(g, Dependency.class)); + schema.addLinkType(MappingSchemas.fromAnnotations(g, Auxiliary.class)); + return schema; + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/IFunction.java b/org.simantics.objmap/src/org/simantics/objmap/IFunction.java new file mode 100644 index 00000000..a2be2908 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/IFunction.java @@ -0,0 +1,13 @@ +package org.simantics.objmap; + +/** + * A generic function object that throws MappingExceptions. + * + * @author Hannu Niemistö + * + * @param Domain of the function + * @param Range of the function + */ +public interface IFunction { + R get(D element) throws MappingException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/ILinkType.java b/org.simantics.objmap/src/org/simantics/objmap/ILinkType.java new file mode 100644 index 00000000..db121ad1 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/ILinkType.java @@ -0,0 +1,21 @@ +package org.simantics.objmap; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; + +/** + * Contains rules for how a link should be created and maintained. + * @author Hannu Niemistö + */ +public interface ILinkType extends IMappingRule { + /** + * Creates a domain element based on known range element. + */ + Resource createDomainElement(WriteGraph g, Object rangeElement) throws MappingException; + + /** + * Creates a range element based on known domain element. + */ + Object createRangeElement(ReadGraph g, Resource domainElement) throws MappingException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/IMapping.java b/org.simantics.objmap/src/org/simantics/objmap/IMapping.java new file mode 100644 index 00000000..64678b88 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/IMapping.java @@ -0,0 +1,127 @@ +package org.simantics.objmap; + +import java.util.Collection; +import java.util.Set; + +import org.simantics.db.Disposable; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; + +/** + * A mapping consists of domain (a set of resources), range (a set of Java objects) and + * a set of links relating them. The mapping is used to propagate modifications of + * domain elements to range and vice versa. + * + * @see Manual + * + * @author Hannu Niemistö + */ +public interface IMapping extends Disposable { + + /** + * Returns the domain of the mapping. All set operations are supported. + * Adding a new domain element does not automatically create a link to it. + * Removal of a domain element removes also a link and the target element, + * but does not remove the element from the database. + */ + Set getDomain(); + + /** + * Returns the range of the mapping. All set operations are supported. + * Adding a new range element does not automatically create a link to it. + * Removal of a range element removes also a link and the domain element, + * but does not remove the domain element from the database. + */ + Set getRange(); + + /** + * Updates all domain elements whose counterpart is modified and creates new + * domain elements for previously added range elements. Returns the + * collection of domain elements that were modified or created in the update + * process. + */ + Collection updateDomain(WriteGraph g) throws MappingException; + + /** + * Updates all range elements whose counterpart is modified and creates new + * range elements for previously added domain elements. Returns the + * collection of range elements that were modified or created in the update + * process. + */ + Collection updateRange(ReadGraph g) throws MappingException; + + /** + * Returns the counterpart of a domain element or null if the element does + * not belong to the domain or does not have a link. + */ + Object get(Resource domainElement); + + /** + * Returns the counterpart of a range element or null if the element does + * not belong to the range or does not have a link. + */ + Resource inverseGet(Object rangeElement); + + /** + * A convenience method that adds a domain element to the mapping and + * immediately updates the mapping and returns the corresponding range + * element. + */ + Object map(ReadGraph g, Resource domainElement) throws MappingException; + + /** + * A convenience method that adds a range element to the mapping and + * immediately updates the mapping and returns the corresponding domain + * element. + */ + Resource inverseMap(WriteGraph g, Object rangeElement) + throws MappingException; + + /** + * Tells the mapping that the domain element has been modified. + */ + void domainModified(Resource domainElement); + + /** + * Tells the mapping that the range element has been modified. + */ + void rangeModified(Object rangeElement); + + /** + * Tells if some domain elements have been modified or added. + */ + boolean isDomainModified(); + + /** + * Tells if some range elements have been modified or added. + */ + boolean isRangeModified(); + + /** + * Returns a collection of domain elements which have been modified and also + * their counterparts in the mapping are modified. These elements are in + * conflict in the sense that the updating domain and range in different + * orders may produce different results. + */ + Collection getConflictingDomainElements(); + + /** + * Returns a collection of range elements which have been modified and also + * their counterparts in the mapping are modified. These elements are in + * conflict in the sense that the updating domain and range in different + * orders may produce different results. + */ + Collection getConflictingRangeElements(); + + /** + * Adds a listener for domain and range modifications. + */ + void addMappingListener(IMappingListener listener); + + /** + * Removes a previously added listener. + */ + void removeMappingListener(IMappingListener listener); + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/IMappingListener.java b/org.simantics.objmap/src/org/simantics/objmap/IMappingListener.java new file mode 100644 index 00000000..766348aa --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/IMappingListener.java @@ -0,0 +1,19 @@ +package org.simantics.objmap; + +/** + * Listens modifications in a mapping. + * @author Hannu Niemistö + */ +public interface IMappingListener { + /** + * Called when some domain element is modified or created + * and the mapping was previously up to date. + */ + void domainModified(); + + /** + * Called when some range element is modified or created + * and the mapping was previously up to date. + */ + void rangeModified(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/IMappingRule.java b/org.simantics.objmap/src/org/simantics/objmap/IMappingRule.java new file mode 100644 index 00000000..211e02f8 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/IMappingRule.java @@ -0,0 +1,33 @@ +package org.simantics.objmap; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; + +/** + * A rule for how domain and range elements are related to each other. + * @author Hannu Niemistö + */ +public interface IMappingRule { + /** + * Modifies the domain element so that it corresponds to the range element. + * @param g write transaction + * @param map unidirectional view of the current mapping + * @param domainElement the domain element that is updated + * @param rangeElement the range element that corresponds to the domain element + * @return true if the rule made some modifications + * @throws MappingException + */ + boolean updateDomain(WriteGraph g, IFunction map, Resource domainElement, Object rangeElement) throws MappingException; + + /** + * Modifies the range element so that it corresponds to the domain element. + * @param g read transaction + * @param map unidirectional view of the current mapping + * @param domainElement the domain element that corresponds to the range element + * @param rangeElement the range element that is updated + * @return true if the rule made some modifications + * @throws MappingException + */ + boolean updateRange(ReadGraph g, IFunction map, Resource domainElement, Object rangeElement) throws MappingException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/IMappingSchema.java b/org.simantics.objmap/src/org/simantics/objmap/IMappingSchema.java new file mode 100644 index 00000000..1dfbe431 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/IMappingSchema.java @@ -0,0 +1,20 @@ +package org.simantics.objmap; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; + +/** + * Specifies the link types of new elements added to a mapping. + * @author Hannu Niemistö + */ +public interface IMappingSchema { + /** + * @return Link type that should be used for the element. + */ + ILinkType linkTypeOfDomainElement(ReadGraph g, Resource element) throws MappingException; + + /** + * @return Link type that should be used for the element. + */ + ILinkType linkTypeOfRangeElement(Object element) throws MappingException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/MappingException.java b/org.simantics.objmap/src/org/simantics/objmap/MappingException.java new file mode 100644 index 00000000..6fa9f6a8 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/MappingException.java @@ -0,0 +1,29 @@ +package org.simantics.objmap; + +import org.simantics.db.exception.DatabaseException; + +/** + * An exception thrown for any error during mapping methods. + * @author Hannu Niemistö + */ +public class MappingException extends DatabaseException { + + private static final long serialVersionUID = -4026122899414272427L; + + public MappingException() { + super(); + } + + public MappingException(String message, Throwable cause) { + super(message, cause); + } + + public MappingException(String message) { + super(message); + } + + public MappingException(Throwable cause) { + super(cause); + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/Mappings.java b/org.simantics.objmap/src/org/simantics/objmap/Mappings.java new file mode 100644 index 00000000..6c576fa6 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/Mappings.java @@ -0,0 +1,38 @@ +package org.simantics.objmap; + +import org.simantics.objmap.impl.Mapping; +import org.simantics.objmap.impl.UnidirectionalMapping; + +/** + * Static utility methods for mappings. + * @author Hannu Niemistö + */ +public class Mappings { + private Mappings() {} + + /** + * Creates a new mapping based on the given mapping schema. + * The created mapping is not thread-safe and will not + * listen database changes automatically. + */ + public static IMapping createWithoutListening(IMappingSchema schema) { + return new Mapping(schema, false); + } + + /** + * Creates a new mapping based on the given mapping schema. + * The created mapping is not thread-safe. It listens database + * changes automatically. + */ + public static IMapping createWithListening(IMappingSchema schema) { + return new Mapping(schema, true); + } + + /** + * Creates a mapping that supports only the direction from domain to range. + * Does not listen the database. + */ + public static IMapping createUnidirectional(IMappingSchema schema) { + return new UnidirectionalMapping(schema); + } +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/Composition.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/Composition.java new file mode 100644 index 00000000..eaf368cf --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/Composition.java @@ -0,0 +1,11 @@ +package org.simantics.objmap.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD,ElementType.METHOD}) +public @interface Composition { +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/GraphType.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/GraphType.java new file mode 100644 index 00000000..d434b3c2 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/GraphType.java @@ -0,0 +1,16 @@ +package org.simantics.objmap.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies the domain type that the class corresponds to. + * @author Hannu Niemistö + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface GraphType { + String value(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElement.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElement.java new file mode 100644 index 00000000..d3d011b4 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElement.java @@ -0,0 +1,16 @@ +package org.simantics.objmap.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.simantics.objmap.annotations.factories.RelatedElementRuleFactory; +import org.simantics.objmap.annotations.meta.HasFieldRuleFactory; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@HasFieldRuleFactory(RelatedElementRuleFactory.class) +public @interface RelatedElement { + String value(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElements.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElements.java new file mode 100644 index 00000000..a619ab60 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedElements.java @@ -0,0 +1,16 @@ +package org.simantics.objmap.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.simantics.objmap.annotations.factories.RelatedElementsRuleFactory; +import org.simantics.objmap.annotations.meta.HasFieldRuleFactory; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@HasFieldRuleFactory(RelatedElementsRuleFactory.class) +public @interface RelatedElements { + String value(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedValue.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedValue.java new file mode 100644 index 00000000..e6138d9a --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/RelatedValue.java @@ -0,0 +1,21 @@ +package org.simantics.objmap.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.simantics.objmap.annotations.factories.RelatedValueRuleFactory; +import org.simantics.objmap.annotations.meta.HasFieldRuleFactory; + +/** + * Specifies a correspondence between a field and + * functional property. + * @author Hannu Niemistö + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@HasFieldRuleFactory(RelatedValueRuleFactory.class) +public @interface RelatedValue { + String value(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/DataTypeUtils.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/DataTypeUtils.java new file mode 100644 index 00000000..8d38fbdd --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/DataTypeUtils.java @@ -0,0 +1,46 @@ +package org.simantics.objmap.annotations.factories; + +import org.simantics.db.Builtins; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; + +public class DataTypeUtils { + + public static Resource dataTypeOfClass(ReadGraph g, Class clazz) { + Builtins b = g.getBuiltins(); + if(clazz.equals(Double.class) || clazz.equals(double.class)) + return b.Double; + else if(clazz.equals(String.class)) + return b.String; + else if(clazz.equals(Integer.class) || clazz.equals(int.class)) + return b.Integer; + else if(clazz.equals(Float.class) || clazz.equals(float.class)) + return b.Float; + else if(clazz.equals(Boolean.class) || clazz.equals(boolean.class)) + return b.Boolean; + else if(clazz.equals(Long.class) || clazz.equals(long.class)) + return b.Long; + else if(clazz.equals(Byte.class) || clazz.equals(byte.class)) + return b.Byte; + + else if(clazz.equals(double[].class)) + return b.DoubleArray; + else if(clazz.equals(int[].class)) + return b.IntegerArray; + else if(clazz.equals(byte[].class)) + return b.ByteArray; + else if(clazz.equals(float[].class)) + return b.FloatArray; + else if(clazz.equals(boolean[].class)) + return b.BooleanArray; + else if(clazz.equals(String[].class)) + return b.StringArray; + else if(clazz.equals(long[].class)) + return b.LongArray; + else { + System.out.println("Couldn't find a data type for " + clazz); + return null; + } + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementRuleFactory.java new file mode 100644 index 00000000..f4499fa4 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementRuleFactory.java @@ -0,0 +1,28 @@ +package org.simantics.objmap.annotations.factories; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.ResourceNotFoundException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.exception.ValidationException; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.annotations.RelatedElement; +import org.simantics.objmap.rules.MappedElementRule; +import org.simantics.objmap.rules.domain.RelatedObjectAccessor; +import org.simantics.objmap.rules.factory.IFieldRuleFactory; +import org.simantics.objmap.rules.range.FieldAccessor; + +public class RelatedElementRuleFactory implements IFieldRuleFactory { + + @Override + public IMappingRule create(ReadGraph g, Annotation _annotation, Field field) throws ResourceNotFoundException, ValidationException, ServiceException { + RelatedElement annotation = (RelatedElement)_annotation; + return new MappedElementRule( + new RelatedObjectAccessor(g.getResourceByURI(annotation.value())), + new FieldAccessor(field) + ); + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementsRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementsRuleFactory.java new file mode 100644 index 00000000..74011e46 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedElementsRuleFactory.java @@ -0,0 +1,29 @@ +package org.simantics.objmap.annotations.factories; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Collection; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.ResourceNotFoundException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.exception.ValidationException; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.annotations.RelatedElements; +import org.simantics.objmap.rules.MappedElementsRule; +import org.simantics.objmap.rules.domain.RelatedObjectsAccessor; +import org.simantics.objmap.rules.factory.IFieldRuleFactory; +import org.simantics.objmap.rules.range.FieldAccessor; + +public class RelatedElementsRuleFactory implements IFieldRuleFactory { + + @Override + public IMappingRule create(ReadGraph g, Annotation _annotation, Field field) throws ResourceNotFoundException, ValidationException, ServiceException { + RelatedElements annotation = (RelatedElements)_annotation; + return new MappedElementsRule( + new RelatedObjectsAccessor(g.getResourceByURI(annotation.value())), + new FieldAccessor>(field) + ); + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedValueRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedValueRuleFactory.java new file mode 100644 index 00000000..7bbecaa4 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/factories/RelatedValueRuleFactory.java @@ -0,0 +1,33 @@ +package org.simantics.objmap.annotations.factories; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import org.simantics.db.Builtins; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.ResourceNotFoundException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.exception.ValidationException; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.annotations.RelatedValue; +import org.simantics.objmap.rules.ValueRule; +import org.simantics.objmap.rules.domain.RelatedValueAccessor; +import org.simantics.objmap.rules.factory.IFieldRuleFactory; +import org.simantics.objmap.rules.range.FieldAccessor; + +public class RelatedValueRuleFactory implements IFieldRuleFactory { + + @Override + public IMappingRule create(ReadGraph g, Annotation _annotation, Field field) throws ResourceNotFoundException, ValidationException, ServiceException { + RelatedValue annotation = (RelatedValue)_annotation; + Builtins b = g.getBuiltins(); + return new ValueRule( + new RelatedValueAccessor( + g.getResourceByURI(annotation.value()), + DataTypeUtils.dataTypeOfClass(g, field.getType()) + ), + new FieldAccessor(field) + ); + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasClassRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasClassRuleFactory.java new file mode 100644 index 00000000..f556f88c --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasClassRuleFactory.java @@ -0,0 +1,14 @@ +package org.simantics.objmap.annotations.meta; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.simantics.objmap.rules.factory.IClassRuleFactory; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface HasClassRuleFactory { + Class value(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasFieldRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasFieldRuleFactory.java new file mode 100644 index 00000000..4f8c8add --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasFieldRuleFactory.java @@ -0,0 +1,14 @@ +package org.simantics.objmap.annotations.meta; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.simantics.objmap.rules.factory.IFieldRuleFactory; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface HasFieldRuleFactory { + Class value(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasMethodRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasMethodRuleFactory.java new file mode 100644 index 00000000..273041b7 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/annotations/meta/HasMethodRuleFactory.java @@ -0,0 +1,14 @@ +package org.simantics.objmap.annotations.meta; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.simantics.objmap.rules.factory.IMethodRuleFactory; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface HasMethodRuleFactory { + Class value(); +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/impl/Link.java b/org.simantics.objmap/src/org/simantics/objmap/impl/Link.java new file mode 100644 index 00000000..45336d9a --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/impl/Link.java @@ -0,0 +1,29 @@ +/** + * + */ +package org.simantics.objmap.impl; + +import org.simantics.db.Resource; +import org.simantics.objmap.ILinkType; + +/** + * An indication that the domain element corresponds to the range element + * in the mapping. The link type describes how source and target objects + * are updated. There are additionally flags for dirtiness of the link. + * @author Hannu Niemistö + */ +public class Link { + public ILinkType type; + public Resource domainElement; + public Object rangeElement; + + public boolean domainModified = false; + public boolean rangeModified = false; + public boolean removed = false; + + public Link(ILinkType type, Resource domainElement, Object rangeElement) { + this.type = type; + this.domainElement = domainElement; + this.rangeElement = rangeElement; + } +} \ No newline at end of file diff --git a/org.simantics.objmap/src/org/simantics/objmap/impl/Mapping.java b/org.simantics.objmap/src/org/simantics/objmap/impl/Mapping.java new file mode 100644 index 00000000..fb31e2c6 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/impl/Mapping.java @@ -0,0 +1,411 @@ +package org.simantics.objmap.impl; + +import gnu.trove.THashMap; +import gnu.trove.TObjectIdentityHashingStrategy; + +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.objmap.IFunction; +import org.simantics.objmap.ILinkType; +import org.simantics.objmap.IMapping; +import org.simantics.objmap.IMappingListener; +import org.simantics.objmap.IMappingSchema; +import org.simantics.objmap.MappingException; + +/** + * An implementation of IMapping. The class should not be created + * directly but using methods in Mappings. + * @see org.simantics.objmap.Mappings + * @author Hannu Niemistö + */ +public class Mapping implements IMapping { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + private static final TObjectIdentityHashingStrategy IDENTITY_HASHING = + new TObjectIdentityHashingStrategy(); + + IMappingSchema schema; + + THashMap domain = new THashMap(); + THashMap range = new THashMap(IDENTITY_HASHING); + ArrayList listeners = new ArrayList(); + + ArrayList modifiedDomainLinks = new ArrayList(); + ArrayList modifiedRangeLinks = new ArrayList(); + boolean disposed = false; + + boolean listensDomain; + + public Mapping(IMappingSchema schema, boolean listensDomain) { + this.schema = schema; + this.listensDomain = listensDomain; + } + + private void removeLink(Link link) { + if(link.domainModified) + modifiedDomainLinks.remove(link); + if(link.rangeModified) + modifiedRangeLinks.remove(link); + link.removed = true; + } + + private void createDomain(WriteGraph g, Link link) throws MappingException { + ILinkType type = schema.linkTypeOfRangeElement(link.rangeElement); + Resource domainElement = type.createDomainElement(g, link.rangeElement); + + link.type = type; + link.domainElement = domainElement; + domain.put(domainElement, link); + } + + private void createRange(ReadGraph g, Link link) throws MappingException { + ILinkType type = schema.linkTypeOfDomainElement(g, link.domainElement); + Object rangeElement = type.createRangeElement(g, link.domainElement); + + link.type = type; + link.rangeElement = rangeElement; + range.put(rangeElement, link); + } + + Set domainSet = new AbstractSet() { + + public boolean add(Resource e) { + if(domain.containsKey(e)) + return false; + Link link = new Link(null, e, null); + domain.put(e, link); + modifiedDomainLinks.add(link); + return true; + } + + public boolean contains(Object o) { + return domain.contains(o); + } + + public boolean remove(Object o) { + Link link = domain.remove(o); + if(link == null) + return false; + removeLink(link); + if(link.rangeElement != null) + range.remove(link.rangeElement); + return true; + } + + @Override + public Iterator iterator() { + // FIXME does not implement Iterator.remove correctly + return domain.keySet().iterator(); + } + + @Override + public int size() { + return domain.size(); + } + + }; + + Set rangeSet = new AbstractSet() { + + public boolean add(Object e) { + if(range.containsKey(e)) + return false; + Link link = new Link(null, null, e); + range.put(e, link); + modifiedRangeLinks.add(link); + return true; + } + + public boolean contains(Object o) { + return range.contains(o); + } + + public boolean remove(Object o) { + Link link = range.remove(o); + if(link == null) + return false; + removeLink(link); + if(link.domainElement != null) + domain.remove(link.domainElement); + return true; + } + + @Override + public Iterator iterator() { + // FIXME does not implement Iterator.remove correctly + return range.keySet().iterator(); + } + + @Override + public int size() { + return range.size(); + } + + }; + + class DomainToRange implements IFunction { + + ReadGraph g; + + public DomainToRange(ReadGraph g) { + this.g = g; + } + + @Override + public Object get(Resource element) throws MappingException { + Link link = domain.get(element); + if(link == null) { + ILinkType type = schema.linkTypeOfDomainElement(g, element); + Object rangeElement = type.createRangeElement(g, element); + + link = new Link(type, element, rangeElement); + domain.put(element, link); + range.put(rangeElement, link); + link.domainModified = true; + modifiedDomainLinks.add(link); + + return rangeElement; + } + else { + if(link.type == null) + createRange(g, link); + return link.rangeElement; + } + } + + }; + + class RangeToDomain implements IFunction { + + WriteGraph g; + + public RangeToDomain(WriteGraph g) { + this.g = g; + } + + @Override + public Resource get(Object element) throws MappingException { + Link link = range.get(element); + if(link == null) { + ILinkType type = schema.linkTypeOfRangeElement(element); + Resource domainElement = type.createDomainElement(g, element); + + link = new Link(type, domainElement, element); + domain.put(domainElement, link); + range.put(element, link); + link.rangeModified = true; + modifiedRangeLinks.add(link); + + return domainElement; + } + else { + if(link.type == null) + createDomain(g, link); + return link.domainElement; + } + } + + }; + + @Override + public Set getDomain() { + return domainSet; + } + + @Override + public Set getRange() { + return rangeSet; + } + + @Override + public Collection updateDomain(WriteGraph g) throws MappingException { + LOGGER.info("Mapping.updateDomain"); + RangeToDomain map = new RangeToDomain(g); + ArrayList updated = new ArrayList(); + while(!modifiedRangeLinks.isEmpty()) { + LOGGER.info(" modifiedRangeLinks.size() = " + modifiedRangeLinks.size()); + + Link link = modifiedRangeLinks.remove(modifiedRangeLinks.size()-1); + link.rangeModified = false; + if(link.domainModified) { + link.domainModified = false; + modifiedDomainLinks.remove(link); + } + + if(link.type == null) { + createDomain(g, link); + } + + if(link.type.updateDomain(g, map, link.domainElement, link.rangeElement)) + updated.add(link.domainElement); + } + return updated; + } + + @Override + public Collection updateRange(ReadGraph g) throws MappingException { + LOGGER.info("Mapping.updateRange"); + DomainToRange map = new DomainToRange(g); + ArrayList updated = new ArrayList(); + while(!modifiedDomainLinks.isEmpty()) { + Link link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1); + link.domainModified = false; + if(link.rangeModified) { + link.rangeModified = false; + modifiedRangeLinks.remove(link); + } + + if(link.type == null) { + createRange(g, link); + } + + if(listensDomain) { + RangeUpdateRequest request = new RangeUpdateRequest(link, map, this); + g.syncRequest(request, request); + // TODO check if really modified + updated.add(link.rangeElement); + } + else + if(link.type.updateRange(g, map, link.domainElement, link.rangeElement)) + updated.add(link.rangeElement); + } + return updated; + } + + @Override + public Object get(Resource domainElement) { + Link link = domain.get(domainElement); + if(link == null) + return null; + return link.rangeElement; + } + + @Override + public Resource inverseGet(Object rangeElement) { + Link link = range.get(rangeElement); + if(link == null) + return null; + return link.domainElement; + } + + @Override + public Resource inverseMap(WriteGraph g, Object rangeElement) throws MappingException { + getRange().add(rangeElement); + updateDomain(g); + return inverseGet(rangeElement); + } + + @Override + public Object map(ReadGraph g, Resource domainElement) throws MappingException { + getDomain().add(domainElement); + updateRange(g); + return get(domainElement); + } + + void domainModified(Link link) { + if(!link.domainModified) { + link.domainModified = true; + modifiedDomainLinks.add(link); + if(modifiedDomainLinks.size() == 1) { + for(IMappingListener listener : listeners) + listener.domainModified(); + } + } + } + + @Override + public void domainModified(Resource domainElement) { + Link link = domain.get(domainElement); + if(link != null) + domainModified(link); + } + + void rangeModified(Link link) { + if(!link.rangeModified) { + link.rangeModified = true; + modifiedRangeLinks.add(link); + if(modifiedRangeLinks.size() == 1) { + for(IMappingListener listener : listeners) + listener.rangeModified(); + } + } + } + + @Override + public void rangeModified(Object rangeElement) { + Link link = range.get(rangeElement); + if(link != null) + rangeModified(link); + } + + @Override + public boolean isDomainModified() { + return !modifiedDomainLinks.isEmpty(); + } + + @Override + public boolean isRangeModified() { + return !modifiedRangeLinks.isEmpty(); + } + + @Override + public void addMappingListener(IMappingListener listener) { + listeners.add(listener); + } + + @Override + public void removeMappingListener(IMappingListener listener) { + listeners.remove(listener); + } + + @Override + public Collection getConflictingDomainElements() { + ArrayList result = new ArrayList(); + if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) { + for(Link link : modifiedDomainLinks) + if(link.rangeModified) + result.add(link.domainElement); + } + else { + for(Link link : modifiedRangeLinks) + if(link.domainModified) + result.add(link.domainElement); + } + return result; + } + + @Override + public Collection getConflictingRangeElements() { + ArrayList result = new ArrayList(); + if(modifiedDomainLinks.size() < modifiedRangeLinks.size()) { + for(Link link : modifiedDomainLinks) + if(link.rangeModified) + result.add(link.rangeElement); + } + else { + for(Link link : modifiedRangeLinks) + if(link.domainModified) + result.add(link.rangeElement); + } + return result; + } + + @Override + public void dispose() { + disposed = true; + } + + public boolean isDisposed() { + return disposed; + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/impl/RangeUpdateRequest.java b/org.simantics.objmap/src/org/simantics/objmap/impl/RangeUpdateRequest.java new file mode 100644 index 00000000..b7578f84 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/impl/RangeUpdateRequest.java @@ -0,0 +1,64 @@ +package org.simantics.objmap.impl; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.procedure.SyncListener; +import org.simantics.db.request.Read; +import org.simantics.objmap.IFunction; +import org.simantics.objmap.MappingException; + +public class RangeUpdateRequest implements Read, SyncListener { + + Link link; + /* + * Note that this map uses a read request that it has got from caller and + * not the one that is used in updateRange. This is intentional. + */ + IFunction map; // map==null is used to flag that request is performed once + Mapping mapping; // mapping==null is used as a flag the request disposed + + public RangeUpdateRequest(Link link, IFunction map, Mapping mapping) { + this.link = link; + this.map = map; + this.mapping = mapping; + } + + @Override + public Boolean perform(ReadGraph g) throws DatabaseException { + if(map != null) { + link.type.updateRange(g, map, link.domainElement, link.rangeElement); + map = null; + return Boolean.TRUE; + } + else if(mapping != null) { + mapping.domainModified(link); + mapping = null; + return Boolean.FALSE; + } + else + return null; + } + + @Override + public void exception(ReadGraph graph, Throwable throwable) + throws DatabaseException { + if(throwable instanceof DatabaseException) + throw (DatabaseException)throwable; + else + throw new MappingException(throwable); + } + + @Override + public void execute(ReadGraph graph, Boolean result) + throws DatabaseException { + } + + @Override + public boolean isDisposed() { + return mapping == null || link.removed || mapping.isDisposed(); + } + + + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/impl/UnidirectionalMapping.java b/org.simantics.objmap/src/org/simantics/objmap/impl/UnidirectionalMapping.java new file mode 100644 index 00000000..79395db9 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/impl/UnidirectionalMapping.java @@ -0,0 +1,254 @@ +package org.simantics.objmap.impl; + +import gnu.trove.THashMap; +import gnu.trove.TObjectIdentityHashingStrategy; + +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.objmap.IFunction; +import org.simantics.objmap.ILinkType; +import org.simantics.objmap.IMapping; +import org.simantics.objmap.IMappingListener; +import org.simantics.objmap.IMappingSchema; +import org.simantics.objmap.MappingException; + +/** + * An implementation of IMapping. The class should not be created + * directly but using methods in Mappings. + * @see org.simantics.objmap.Mappings + * @author Hannu Niemistö + */ +public class UnidirectionalMapping implements IMapping { + + private static final TObjectIdentityHashingStrategy IDENTITY_HASHING = + new TObjectIdentityHashingStrategy(); + + IMappingSchema schema; + + THashMap domain = new THashMap(); + ArrayList listeners = new ArrayList(); + + ArrayList modifiedDomainLinks = new ArrayList(); + boolean disposed = false; + + public UnidirectionalMapping(IMappingSchema schema) { + this.schema = schema; + } + + private void removeLink(Link link) { + if(link.domainModified) + modifiedDomainLinks.remove(link); + link.removed = true; + } + + private void createDomain(WriteGraph g, Link link) throws MappingException { + ILinkType type = schema.linkTypeOfRangeElement(link.rangeElement); + Resource domainElement = type.createDomainElement(g, link.rangeElement); + + link.type = type; + link.domainElement = domainElement; + domain.put(domainElement, link); + } + + private void createRange(ReadGraph g, Link link) throws MappingException { + ILinkType type = schema.linkTypeOfDomainElement(g, link.domainElement); + Object rangeElement = type.createRangeElement(g, link.domainElement); + + link.type = type; + link.rangeElement = rangeElement; + } + + Set domainSet = new AbstractSet() { + + public boolean add(Resource e) { + if(domain.containsKey(e)) + return false; + Link link = new Link(null, e, null); + domain.put(e, link); + modifiedDomainLinks.add(link); + return true; + } + + public boolean contains(Object o) { + return domain.contains(o); + } + + public boolean remove(Object o) { + Link link = domain.remove(o); + if(link == null) + return false; + removeLink(link); + return true; + } + + @Override + public Iterator iterator() { + // FIXME does not implement Iterator.remove correctly + return domain.keySet().iterator(); + } + + @Override + public int size() { + return domain.size(); + } + + }; + + class DomainToRange implements IFunction { + + ReadGraph g; + + public DomainToRange(ReadGraph g) { + this.g = g; + } + + @Override + public Object get(Resource element) throws MappingException { + Link link = domain.get(element); + if(link == null) { + ILinkType type = schema.linkTypeOfDomainElement(g, element); + Object rangeElement = type.createRangeElement(g, element); + + link = new Link(type, element, rangeElement); + domain.put(element, link); + link.domainModified = true; + modifiedDomainLinks.add(link); + + return rangeElement; + } + else { + if(link.type == null) + createRange(g, link); + return link.rangeElement; + } + } + + }; + + @Override + public Set getDomain() { + return domainSet; + } + + @Override + public Set getRange() { + throw new UnsupportedOperationException(); + } + + @Override + public Collection updateDomain(WriteGraph g) throws MappingException { + throw new UnsupportedOperationException(); + } + + @Override + public Collection updateRange(ReadGraph g) throws MappingException { + DomainToRange map = new DomainToRange(g); + ArrayList updated = new ArrayList(); + while(!modifiedDomainLinks.isEmpty()) { + Link link = modifiedDomainLinks.remove(modifiedDomainLinks.size()-1); + link.domainModified = false; + if(link.type == null) { + createRange(g, link); + } + + link.type.updateRange(g, map, link.domainElement, link.rangeElement); + updated.add(link.rangeElement); + } + return updated; + } + + @Override + public Object get(Resource domainElement) { + Link link = domain.get(domainElement); + if(link == null) + return null; + return link.rangeElement; + } + + @Override + public Resource inverseGet(Object rangeElement) { + throw new UnsupportedOperationException(); + } + + @Override + public Resource inverseMap(WriteGraph g, Object rangeElement) throws MappingException { + throw new UnsupportedOperationException(); + } + + @Override + public Object map(ReadGraph g, Resource domainElement) throws MappingException { + getDomain().add(domainElement); + updateRange(g); + return get(domainElement); + } + + void domainModified(Link link) { + if(!link.domainModified) { + link.domainModified = true; + modifiedDomainLinks.add(link); + if(modifiedDomainLinks.size() == 1) { + for(IMappingListener listener : listeners) + listener.domainModified(); + } + } + } + + @Override + public void domainModified(Resource domainElement) { + Link link = domain.get(domainElement); + if(link != null) + domainModified(link); + } + + @Override + public boolean isDomainModified() { + return !modifiedDomainLinks.isEmpty(); + } + + @Override + public boolean isRangeModified() { + throw new UnsupportedOperationException(); + } + + @Override + public void addMappingListener(IMappingListener listener) { + listeners.add(listener); + } + + @Override + public void removeMappingListener(IMappingListener listener) { + listeners.remove(listener); + } + + @Override + public Collection getConflictingDomainElements() { + throw new UnsupportedOperationException(); + } + + @Override + public Collection getConflictingRangeElements() { + throw new UnsupportedOperationException(); + } + + @Override + public void dispose() { + disposed = true; + } + + public boolean isDisposed() { + return disposed; + } + + @Override + public void rangeModified(Object rangeElement) { + throw new UnsupportedOperationException(); + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementRule.java b/org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementRule.java new file mode 100644 index 00000000..51a3e6e5 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementRule.java @@ -0,0 +1,55 @@ +package org.simantics.objmap.rules; + +import java.util.ArrayList; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.objmap.IFunction; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.MappingException; +import org.simantics.objmap.rules.domain.IDomainAccessor; +import org.simantics.objmap.rules.range.IRangeAccessor; + +/** + * A rule that synchronizes collection of elements between + * domain and range accessors. Elements are mapped from + * between domain and range during the synchronization. + * @author Hannu Niemistö + */ +public class MappedElementRule implements IMappingRule { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + IDomainAccessor domainAccessor; + IRangeAccessor rangeAccessor; + + public MappedElementRule(IDomainAccessor domainAccessor, + IRangeAccessor rangeAccessor) { + this.domainAccessor = domainAccessor; + this.rangeAccessor = rangeAccessor; + } + + @Override + public boolean updateDomain(WriteGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + LOGGER.info(" MappedElementRule.updateDomain"); + Object value = rangeAccessor.get(rangeElement); + Resource mappedValue = value == null ? null : map.get(value); + return domainAccessor.set(g, domainElement, mappedValue); + } + + @Override + public boolean updateRange(ReadGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + LOGGER.info(" MappedElementRule.updateRange"); + Resource value = domainAccessor.get(g, domainElement); + Object mappedValue = value == null ? null : map.get(value); + return rangeAccessor.set(rangeElement, mappedValue); + } +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementsRule.java b/org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementsRule.java new file mode 100644 index 00000000..e37ce133 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/MappedElementsRule.java @@ -0,0 +1,60 @@ +package org.simantics.objmap.rules; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.objmap.IFunction; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.MappingException; +import org.simantics.objmap.rules.domain.IDomainAccessor; +import org.simantics.objmap.rules.range.IRangeAccessor; + +/** + * A rule that synchronizes collection of elements between + * domain and range accessors. Elements are mapped from + * between domain and range during the synchronization. + * @author Hannu Niemistö + */ +public class MappedElementsRule implements IMappingRule { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + IDomainAccessor> domainAccessor; + IRangeAccessor> rangeAccessor; + + public MappedElementsRule(IDomainAccessor> domainAccessor, + IRangeAccessor> rangeAccessor) { + this.domainAccessor = domainAccessor; + this.rangeAccessor = rangeAccessor; + } + + @Override + public boolean updateDomain(WriteGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + LOGGER.info(" MappedElementsRule.updateDomain"); + Collection value = rangeAccessor.get(rangeElement); + ArrayList mappedValue = new ArrayList(value.size()); + for(Object obj : value) + mappedValue.add(map.get(obj)); + return domainAccessor.set(g, domainElement, mappedValue); + } + + @Override + public boolean updateRange(ReadGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + LOGGER.info(" MappedElementsRule.updateRange"); + Collection value = domainAccessor.get(g, domainElement); + ArrayList mappedValue = new ArrayList(value.size()); + for(Resource r : value) + mappedValue.add(map.get(r)); + return rangeAccessor.set(rangeElement, mappedValue); + } +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/ValueRule.java b/org.simantics.objmap/src/org/simantics/objmap/rules/ValueRule.java new file mode 100644 index 00000000..13e46703 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/ValueRule.java @@ -0,0 +1,52 @@ +package org.simantics.objmap.rules; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.exception.ValidationException; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.objmap.IFunction; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.MappingException; +import org.simantics.objmap.rules.domain.IDomainAccessor; +import org.simantics.objmap.rules.range.IRangeAccessor; + +/** + * A rule that synchronizes values between domain and + * range accessors. + * @author Hannu Niemistö + */ +public class ValueRule implements IMappingRule { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + IDomainAccessor domainAccessor; + IRangeAccessor rangeAccessor; + + public ValueRule(IDomainAccessor domainAccessor, + IRangeAccessor rangeAccessor) { + this.domainAccessor = domainAccessor; + this.rangeAccessor = rangeAccessor; + } + + @Override + public boolean updateDomain(WriteGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + LOGGER.info(" ValueRule.updateDomain"); + Object value = rangeAccessor.get(rangeElement); + return domainAccessor.set(g, domainElement, value); + } + + @Override + public boolean updateRange(ReadGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + LOGGER.info(" ValueRule.updateRange"); + Object value = domainAccessor.get(g, domainElement); + return rangeAccessor.set(rangeElement, value); + } +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/domain/IDomainAccessor.java b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/IDomainAccessor.java new file mode 100644 index 00000000..abb50e71 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/IDomainAccessor.java @@ -0,0 +1,15 @@ +package org.simantics.objmap.rules.domain; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.objmap.MappingException; + +/** + * Provides access to some property of domain elements. + * @author Hannu Niemistö + */ +public interface IDomainAccessor { + T get(ReadGraph g, Resource element) throws MappingException; + boolean set(WriteGraph g, Resource element, T value) throws MappingException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/domain/MappingUtils.java b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/MappingUtils.java new file mode 100644 index 00000000..2a6ef5bb --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/MappingUtils.java @@ -0,0 +1,74 @@ +package org.simantics.objmap.rules.domain; + +import java.util.Arrays; +import java.util.Collection; + +import org.apache.log4j.Logger; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; + +/** + * Static utility methods for rule implementations. + * @author Hannu Niemistö + */ +public class MappingUtils { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + /** + * Adds and removes statements to/from the database so that objects + * will be exactly the objects connected to subject by predicate. + * Returns true if the method made modifications to the database. + */ + public static boolean synchronizeStatements(WriteGraph g, Resource subject, Resource predicate, Resource[] objects) + throws DatabaseException { + Collection currentObjects0 = g.getObjects(subject, predicate); + Resource[] currentObjects = currentObjects0.toArray(new Resource[currentObjects0.size()]); + + Arrays.sort(objects); + Arrays.sort(currentObjects); + + boolean modified = false; + int i=0, j=0; + if(currentObjects.length > 0 && objects.length > 0) + while(true) { + int cmp = currentObjects[i].compareTo(objects[j]); + if(cmp < 0) { + LOGGER.info(" remove statement"); + g.deny(subject, predicate, currentObjects[i]); + modified = true; + ++i; + if(i >= currentObjects.length) + break; + } + else if(cmp > 0) { + LOGGER.info(" add statement"); + g.claim(subject, predicate, objects[j]); + modified = true; + ++j; + if(j >= objects.length) + break; + } + else { + ++i; ++j; + if(i >= currentObjects.length) + break; + if(j >= objects.length) + break; + } + } + while(i < currentObjects.length) { + g.deny(subject, predicate, currentObjects[i]); + modified = true; + ++i; + } + while(j < objects.length) { + g.claim(subject, predicate, objects[j]); + modified = true; + ++j; + } + return modified; + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectAccessor.java b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectAccessor.java new file mode 100644 index 00000000..5dfdaef4 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectAccessor.java @@ -0,0 +1,60 @@ +package org.simantics.objmap.rules.domain; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.MappingException; + +/** + * Accesses a resource attached to the element by given functional relation. + * @author Hannu Niemistö + */ +public class RelatedObjectAccessor implements IDomainAccessor { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + Resource relation; + + public RelatedObjectAccessor(Resource relation) { + this.relation = relation; + } + + @Override + public Resource get(ReadGraph g, Resource element) throws MappingException { + try { + LOGGER.info(" RelatedObjectAccessor.get"); + return g.getPossibleObject(element, relation); + } catch (DatabaseException e) { + throw new MappingException(e); + } + } + + @Override + public boolean set(WriteGraph g, Resource element, Resource value) + throws MappingException { + try { + LOGGER.info(" RelatedObjectAccessor.set"); + Resource resource = g.getPossibleObject(element, relation); + if(resource == null) { + if(value == null) + return false; + g.claim(element, relation, value); + return true; + } + else if(resource.equals(value)) + return false; + else { + g.deny(element, relation); + if(value != null) + g.claim(element, relation, value); + return true; + } + } catch (DatabaseException e) { + throw new MappingException(e); + } + + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectsAccessor.java b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectsAccessor.java new file mode 100644 index 00000000..9ea65731 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedObjectsAccessor.java @@ -0,0 +1,48 @@ +package org.simantics.objmap.rules.domain; + +import java.util.Collection; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.MappingException; + +/** + * Accesses the set of objects attached to the element by the given relation. + * @author Hannu Niemistö + */ +public class RelatedObjectsAccessor implements IDomainAccessor> { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + Resource relation; + + public RelatedObjectsAccessor(Resource relation) { + this.relation = relation; + } + + @Override + public Collection get(ReadGraph g, Resource element) throws MappingException { + try { + LOGGER.info(" RelatedObjectsAccessor.get"); + return g.getObjects(element, relation); + } catch (DatabaseException e) { + throw new MappingException(e); + } + } + + @Override + public boolean set(WriteGraph g, Resource element, Collection value) + throws MappingException { + try { + LOGGER.info(" RelatedObjectsAccessor.set"); + return MappingUtils.synchronizeStatements(g, element, relation, value.toArray(new Resource[value.size()])); + } catch (DatabaseException e) { + throw new MappingException(e); + } + + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedValueAccessor.java b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedValueAccessor.java new file mode 100644 index 00000000..5cad1b86 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/domain/RelatedValueAccessor.java @@ -0,0 +1,72 @@ +package org.simantics.objmap.rules.domain; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.MappingException; + +/** + * Accesses a value attached to the element by given functional relation. + * @author Hannu Niemistö + */ +public class RelatedValueAccessor implements IDomainAccessor { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + Resource relation; + Resource valueType; + + public RelatedValueAccessor(Resource relation, Resource valueType) { + this.relation = relation; + this.valueType = valueType; + } + + @Override + public Object get(ReadGraph g, Resource element) throws MappingException { + try { + LOGGER.info(" RelatedValueAccessor.get"); + Resource valueResource = g.getPossibleObject(element, relation); + if(valueResource == null) + return null; + return g.getValue(valueResource); + } catch (DatabaseException e) { + throw new MappingException(e); + } + } + + @Override + public boolean set(WriteGraph g, Resource element, Object value) + throws MappingException { + try { + LOGGER.info(" RelatedValueAccessor.set"); + Resource valueResource = g.getPossibleObject(element, relation); + if(valueResource == null) { + if(value == null) + return false; + valueResource = g.newResource(); + g.claim(valueResource, g.getBuiltins().InstanceOf, + valueType); + g.claim(element, relation, valueResource); + g.claimValue(valueResource, value); + return true; + } + else { + if(value == null) { + g.deny(valueResource); + return true; + } + Object currentValue = g.getValue(valueResource); + if(currentValue.equals(value)) + return false; + g.claimValue(valueResource, value); + return true; + } + } catch (DatabaseException e) { + throw new MappingException(e); + } + + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IClassRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IClassRuleFactory.java new file mode 100644 index 00000000..20fcce66 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IClassRuleFactory.java @@ -0,0 +1,11 @@ +package org.simantics.objmap.rules.factory; + +import java.lang.annotation.Annotation; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.IMappingRule; + +public interface IClassRuleFactory { + IMappingRule create(ReadGraph g, Annotation annotation, Class clazz) throws DatabaseException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IFieldRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IFieldRuleFactory.java new file mode 100644 index 00000000..8e95a060 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IFieldRuleFactory.java @@ -0,0 +1,12 @@ +package org.simantics.objmap.rules.factory; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.IMappingRule; + +public interface IFieldRuleFactory { + IMappingRule create(ReadGraph g, Annotation annotation, Field field) throws DatabaseException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IMethodRuleFactory.java b/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IMethodRuleFactory.java new file mode 100644 index 00000000..61d26a9d --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/factory/IMethodRuleFactory.java @@ -0,0 +1,12 @@ +package org.simantics.objmap.rules.factory; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.IMappingRule; + +public interface IMethodRuleFactory { + IMappingRule create(ReadGraph g, Annotation annotation, Method method) throws DatabaseException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/range/FieldAccessor.java b/org.simantics.objmap/src/org/simantics/objmap/rules/range/FieldAccessor.java new file mode 100644 index 00000000..83a4bfd0 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/range/FieldAccessor.java @@ -0,0 +1,63 @@ +package org.simantics.objmap.rules.range; + +import java.lang.reflect.Field; + +import org.apache.log4j.Logger; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.objmap.MappingException; + +/** + * Accesses the given field of the element. + * @author Hannu Niemistö + */ +public class FieldAccessor implements IRangeAccessor { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + Field field; + + public FieldAccessor(Field field) { + this.field = field; + } + + @Override + public T get(Object element) throws MappingException { + try { + T result = (T)field.get(element); + + if(LOGGER.isInfoEnabled()) + LOGGER.info(" FieldAccessor.get " + + field.getName() + " -> " + result + ); + + return result; + } catch (IllegalArgumentException e) { + throw new MappingException(e); + } catch (IllegalAccessException e) { + throw new MappingException(e); + } + } + + @Override + public boolean set(Object element, T value) throws MappingException { + try { + Object currentValue = field.get(element); + + if(LOGGER.isInfoEnabled()) + LOGGER.info(" FieldAccessor.set " + + field.getName() + " " + currentValue + + " -> " + value + ); + + if(currentValue == null ? value == null : currentValue.equals(value)) + return false; + field.set(element, value); + return true; + } catch (IllegalArgumentException e) { + throw new MappingException(e); + } catch (IllegalAccessException e) { + throw new MappingException(e); + } + } +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/rules/range/IRangeAccessor.java b/org.simantics.objmap/src/org/simantics/objmap/rules/range/IRangeAccessor.java new file mode 100644 index 00000000..d46d6b71 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/rules/range/IRangeAccessor.java @@ -0,0 +1,12 @@ +package org.simantics.objmap.rules.range; + +import org.simantics.objmap.MappingException; + +/** + * Provides access to some property of range elements. + * @author Hannu Niemistö + */ +public interface IRangeAccessor { + T get(Object element) throws MappingException; + boolean set(Object element, T value) throws MappingException; +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/schema/MappingSchemas.java b/org.simantics.objmap/src/org/simantics/objmap/schema/MappingSchemas.java new file mode 100644 index 00000000..472915bf --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/schema/MappingSchemas.java @@ -0,0 +1,83 @@ +package org.simantics.objmap.schema; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; + +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.annotations.GraphType; +import org.simantics.objmap.annotations.RelatedValue; +import org.simantics.objmap.annotations.meta.HasClassRuleFactory; +import org.simantics.objmap.annotations.meta.HasFieldRuleFactory; +import org.simantics.objmap.annotations.meta.HasMethodRuleFactory; + +/** + * Static utility methods for mapping schemas + * @author Hannu Niemistö + */ +public class MappingSchemas { + + /** + * Creates a new SimpleLinkType based on the annotations in the given class. + * @throws IllegalAccessException + * @throws InstantiationException + * @see GraphType + * @see RelatedValue + */ + public static SimpleLinkType fromAnnotations(ReadGraph g, Class clazz) throws DatabaseException, InstantiationException, IllegalAccessException { + GraphType graphType = clazz.getAnnotation(GraphType.class); + + ArrayList rules = new ArrayList(); + collectRulesFromAnnotations(g, clazz, rules); + + return new SimpleLinkType( + g.getResourceByURI(graphType.value()), + clazz, rules); + } + + public static void collectRulesFromAnnotations(ReadGraph g, Class clazz, Collection rules) throws DatabaseException, InstantiationException, IllegalAccessException { + Class superclass = clazz.getSuperclass(); + if(superclass != null) + collectRulesFromAnnotations(g, superclass, rules); + + for(Annotation annotation : clazz.getAnnotations()) { + HasClassRuleFactory factory = + annotation.annotationType().getAnnotation(HasClassRuleFactory.class); + if(factory != null) { + rules.add(factory.value().newInstance() + .create(g, annotation, clazz)); + } + } + + for(Field f : clazz.getDeclaredFields()) { + f.setAccessible(true); + + for(Annotation annotation : f.getAnnotations()) { + HasFieldRuleFactory factory = + annotation.annotationType().getAnnotation(HasFieldRuleFactory.class); + if(factory != null) { + rules.add(factory.value().newInstance() + .create(g, annotation, f)); + } + } + } + + for(Method m : clazz.getDeclaredMethods()) { + m.setAccessible(true); + + for(Annotation annotation : m.getAnnotations()) { + HasMethodRuleFactory factory = + annotation.annotationType().getAnnotation(HasMethodRuleFactory.class); + if(factory != null) { + rules.add(factory.value().newInstance() + .create(g, annotation, m)); + } + } + } + } + +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/schema/SimpleLinkType.java b/org.simantics.objmap/src/org/simantics/objmap/schema/SimpleLinkType.java new file mode 100644 index 00000000..1506834d --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/schema/SimpleLinkType.java @@ -0,0 +1,121 @@ +package org.simantics.objmap.schema; + +import java.util.ArrayList; + +import org.apache.log4j.Logger; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.objmap.IFunction; +import org.simantics.objmap.ILinkType; +import org.simantics.objmap.IMappingRule; +import org.simantics.objmap.MappingException; + +/** + * A link type that is associated with single domain and range type (class). + * SimpleLinkType is composed of simpler rules whose combination determines + * its update policy. + * @author Hannu Niemistö + */ +public class SimpleLinkType implements ILinkType { + + static Logger LOGGER = Logger.getLogger("org.simantics.objmap"); + + Resource domainType; + Class rangeType; + ArrayList rules; + + public SimpleLinkType(Resource domainType, Class rangeType, + ArrayList rules) { + this.domainType = domainType; + this.rangeType = rangeType; + this.rules = rules; + } + + public SimpleLinkType(Resource domainType, Class rangeType) { + this(domainType, rangeType, new ArrayList()); + } + + /** + * Adds a new rule to this link type that is enforced + * during updates. + */ + public void addRule(IMappingRule rule) { + rules.add(rule); + } + + @Override + public Resource createDomainElement(WriteGraph g, Object rangeElement) + throws MappingException { + try { + if(LOGGER.isInfoEnabled()) + LOGGER.info("SimpleLinkType.createDomainElement " + + rangeElement.toString() + ); + Resource result = g.newResource(); + g.claim(result, g.getBuiltins().InstanceOf, domainType); + return result; + } catch(DatabaseException e) { + throw new MappingException(e); + } + } + @Override + public Object createRangeElement(ReadGraph g, Resource domainElement) + throws MappingException { + try { + if(LOGGER.isInfoEnabled()) + try { + LOGGER.info("SimpleLinkType.createRangeElement " + + GraphUtils.getReadableName(g, domainElement) + ); + } catch(DatabaseException e) { + throw new MappingException(e); + } + return rangeType.newInstance(); + } catch (InstantiationException e) { + throw new MappingException(e); + } catch (IllegalAccessException e) { + throw new MappingException(e); + } + } + @Override + public boolean updateDomain(WriteGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + if(LOGGER.isInfoEnabled()) + try { + LOGGER.info("SimpleLinkType.updateDomain " + + GraphUtils.getReadableName(g, domainElement) + " " + + rangeElement.toString() + ); + } catch(DatabaseException e) { + throw new MappingException(e); + } + + boolean updated = false; + for(IMappingRule rule : rules) + updated |= rule.updateDomain(g, map, domainElement, rangeElement); + return updated; + } + @Override + public boolean updateRange(ReadGraph g, IFunction map, + Resource domainElement, Object rangeElement) + throws MappingException { + if(LOGGER.isInfoEnabled()) + try { + LOGGER.info("SimpleLinkType.updateRange " + + GraphUtils.getReadableName(g, domainElement) + " " + + rangeElement.toString() + ); + } catch(DatabaseException e) { + throw new MappingException(e); + } + + boolean updated = false; + for(IMappingRule rule : rules) + updated |= rule.updateRange(g, map, domainElement, rangeElement); + return updated; + } +} diff --git a/org.simantics.objmap/src/org/simantics/objmap/schema/SimpleSchema.java b/org.simantics.objmap/src/org/simantics/objmap/schema/SimpleSchema.java new file mode 100644 index 00000000..51280162 --- /dev/null +++ b/org.simantics.objmap/src/org/simantics/objmap/schema/SimpleSchema.java @@ -0,0 +1,55 @@ +package org.simantics.objmap.schema; + +import gnu.trove.THashMap; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.layer0.utils.direct.GraphUtils; +import org.simantics.objmap.ILinkType; +import org.simantics.objmap.IMappingSchema; +import org.simantics.objmap.MappingException; + +/** + * An implementation of IMappingSchema that contains + * only SimpleLinkTypes. The link type of any domain + * element is based solely on its type in database and + * the link type of any range element is based on its class. + * @author Hannu Niemistö + */ +public class SimpleSchema implements IMappingSchema { + + THashMap domainLinkTypes = + new THashMap(); + THashMap, SimpleLinkType> rangeLinkTypes = + new THashMap, SimpleLinkType>(); + + public void addLinkType(SimpleLinkType linkType) { + domainLinkTypes.put(linkType.domainType, linkType); + rangeLinkTypes.put(linkType.rangeType, linkType); + } + + @Override + public ILinkType linkTypeOfDomainElement(ReadGraph g, Resource element) throws MappingException { + try { + ILinkType type = domainLinkTypes.get( + g.getSingleObject(element, g.getBuiltins().InstanceOf)); + if(type == null) + throw new MappingException("Didn't find a link type for " + + GraphUtils.getReadableName(g, element) + "." + ); + return type; + } catch (DatabaseException e) { + throw new MappingException(e); + } + } + + @Override + public ILinkType linkTypeOfRangeElement(Object element) throws MappingException { + ILinkType type = rangeLinkTypes.get(element.getClass()); + if(type == null) + throw new MappingException("Didn't find a link type for " + element + "."); + return type; + } + +} -- 2.47.1