Difference between revisions of "Resource Adaptation"

From Developer Documents
Jump to navigation Jump to search
Line 4: Line 4:
 
computers to understand the language, this means some code. Here is a basic example:
 
computers to understand the language, this means some code. Here is a basic example:
  
<blockquote>
+
<pre style="white-space: pre-wrap;">
<source lang="java">
 
 
public class ShapeFactory {
 
public class ShapeFactory {
 
     public static Shape create(Graph g, Resource r) {
 
     public static Shape create(Graph g, Resource r) {
Line 23: Line 22:
 
     }
 
     }
 
}
 
}
</source>
+
</pre>
</blockquote>
 
  
 
This works as long as there is only a fixed set of shapes. Addition of every new shape needs modification to the code.
 
This works as long as there is only a fixed set of shapes. Addition of every new shape needs modification to the code.
Line 36: Line 34:
  
 
Declarations are written into adapters.xml-file that has to be located in the plugin root:
 
Declarations are written into adapters.xml-file that has to be located in the plugin root:
<blockquote>
+
<pre style="white-space: pre-wrap;">
<source lang="xml">
 
 
  <?xml version="1.0" encoding="UTF-8"?>
 
  <?xml version="1.0" encoding="UTF-8"?>
 
  <adapters>
 
  <adapters>
Line 70: Line 67:
  
 
  </adapters>
 
  </adapters>
</source>
+
</pre>
</blockquote>
 
  
 
Each <code><target>...</target></code> section defines adaption to given interface.
 
Each <code><target>...</target></code> section defines adaption to given interface.
Line 77: Line 73:
 
<code><baseType></code> gives one or more types where all classes implementing the adaption have been inherited from.
 
<code><baseType></code> gives one or more types where all classes implementing the adaption have been inherited from.
  
<code><type></code> defines direct adaptation from instances of given type ('''uri''') by construction the specified class ('''class''') through Java Reflection API's whereas <code><adapter></code> defines adaptation from instances of given type ('''uri''') by using the given adapter ('''adapterClass'''). The adapter has
+
<code><type></code> defines direct adaptation from instances of given type ('''uri''') by construction the specified class ('''class''') through Java Reflection API's whereas <code><adapter></code> defines adaptation from instances of given type ('''uri''') by using the given adapter ('''adapterClass'''). The adapter has to implement the following interface:
<blockquote>
+
<pre style="white-space: pre-wrap;">
<source lang="java">
 
to implement the following interface:
 
 
  public interface Adapter<T> {
 
  public interface Adapter<T> {
 
     void adapt(AsyncReadGraph g, Resource r, AsyncProcedure<T> procedure);
 
     void adapt(AsyncReadGraph g, Resource r, AsyncProcedure<T> procedure);
 
  }
 
  }
</source>
+
</pre>
</blockquote>
 
 
In addition the adapter may contain this kind of definitions:
 
In addition the adapter may contain this kind of definitions:
<blockquote>
+
<pre style="white-space: pre-wrap;">
<source lang="xml">
 
 
  <resource uri="http://www.simantics.org/Layer0-1.0/OrderedSetElements"
 
  <resource uri="http://www.simantics.org/Layer0-1.0/OrderedSetElements"
 
           class="org.simantics.layer0.utils.binaryPredicates.OrderedSetElementsPredicate"/>
 
           class="org.simantics.layer0.utils.binaryPredicates.OrderedSetElementsPredicate"/>
</source>
+
</pre>
</blockquote>
 
 
This means that the resource defined by the uri is always adapted to an instance of the given class.
 
This means that the resource defined by the uri is always adapted to an instance of the given class.
  
Line 98: Line 89:
  
 
Defined adapters can be used with the following synchronous methods, where <code>clazz</code>-parameter refers to the interface in the adapter declaration:
 
Defined adapters can be used with the following synchronous methods, where <code>clazz</code>-parameter refers to the interface in the adapter declaration:
<blockquote>
+
<pre style="white-space: pre-wrap;">
<source lang="java">
 
 
public interface ReadGraph {
 
public interface ReadGraph {
 
     ....
 
     ....
Line 108: Line 98:
 
     ...
 
     ...
 
}
 
}
</source>
+
</pre>
</blockquote>
 
 
For details, see [[svn:db/trunk/org.simantics.db/src/org/simantics/db/ReadGraph.java|ReadGraph interface]].
 
For details, see [[svn:db/trunk/org.simantics.db/src/org/simantics/db/ReadGraph.java|ReadGraph interface]].
  
 
In addition to <code><adapter></code>-elements, the file may contain <code><installer></code>-elements, with ''class''-attribute referring to an implementation of the interface:
 
In addition to <code><adapter></code>-elements, the file may contain <code><installer></code>-elements, with ''class''-attribute referring to an implementation of the interface:
<blockquote>
+
<pre style="white-space: pre-wrap;">
<source lang="java">
 
 
public interface AdapterInstaller {
 
public interface AdapterInstaller {
 
     void install(ReadGraph g, AdaptionService service) throws Exception;
 
     void install(ReadGraph g, AdaptionService service) throws Exception;
 
}
 
}
</source>
+
</pre>
</blockquote>
 
 
where
 
where
<blockquote>
+
<pre style="white-space: pre-wrap;">
<source lang="java">
 
 
public interface AdaptionService {
 
public interface AdaptionService {
 
     <T> void adapt(AsyncReadGraph g, Resource r, Class<T> clazz, boolean possible, AsyncProcedure<T> procedure);
 
     <T> void adapt(AsyncReadGraph g, Resource r, Class<T> clazz, boolean possible, AsyncProcedure<T> procedure);
Line 130: Line 116:
 
     <T> void addInstanceAdapter(Resource resource, Class<T> clazz, Adapter<T> adapter);     
 
     <T> void addInstanceAdapter(Resource resource, Class<T> clazz, Adapter<T> adapter);     
 
}
 
}
</source>
+
</pre>
</blockquote>
 
 
The class installs adapters by using method <code>addAdapter</code> (and <code>addInstanceAdapter</code>).
 
The class installs adapters by using method <code>addAdapter</code> (and <code>addInstanceAdapter</code>).
 
You should use the xml-file to define your adapters if possible. The installer-mechanism is provided for cases where you want to install an adapter that is constructed with special parameters.
 
You should use the xml-file to define your adapters if possible. The installer-mechanism is provided for cases where you want to install an adapter that is constructed with special parameters.

Revision as of 11:44, 6 October 2010

Problem Statement

No language can define itself. There have to be extra-language mechanisms for defining basic concepts of the language. For computers to understand the language, this means some code. Here is a basic example:

public class ShapeFactory {
    public static Shape create(Graph g, Resource r) {
        ShapeResources sr = ShapeResource.getInstance(g);
        if(g.isInstanceOf(r, sr.Rectangle)) {
            ...
            return new Rectangle2D.Double(...);
        }
        else if(g.isInstanceOf(r, sr.Ellipse)) {
            ...
            return new Ellipse2D.Double(...);
        }
        ...
        else {
            throw new IllegalArgumentException("Resource is not a shape or the implementation does not support it.");
        }
    }
}

This works as long as there is only a fixed set of shapes. Addition of every new shape needs modification to the code. We would like to have something like:

  • A declaration telling that instances of Shape can be adapted to objects of Shape class.
  • For each concrete subtype of Shape, a code creating an object of the corresponding Shape class.
  • Declarations connecting subtypes and code snippets.
  • Utility that implements the creation pattern.

Solution

Declarations are written into adapters.xml-file that has to be located in the plugin root:

 <?xml version="1.0" encoding="UTF-8"?>
 <adapters>
  
     <target interface="org.simantics.form.model.IWidgetModel">
         <baseType uri = "http://www.simantics.org/Form-1.0/Widget"/>
         <type uri = "http://www.simantics.org/Form-1.0/Label"
               class = "org.simantics.form.model.adapters.LabelAdapter"/>
         <type uri = "http://www.simantics.org/Form-1.0/InputText"
               class = "org.simantics.form.model.adapters.InputTextAdapter"/>
         ...
     </target>
     
     <target interface="org.simantics.form.model.ILayoutModel">
         <baseType uri = "http://www.simantics.org/Form-1.0/Layout"/>
 
         <!-- Direct adaption to the specified class via constructor and possible arguments -->
         <type uri = "http://www.simantics.org/Form-1.0/GridLayout"
               class = "org.simantics.form.model.adapters.GridLayout">
             <graph/>
             <this/>
         </type>
  
         <!--
             A procedural adapter definition. adapterClass implements
             org.simantics.db.adaption.Adapter to construct an instance
             of the target interface.
         -->
         <adapter uri="http://www.simantics.org/Form-1.0/FormLayout"
               adapterClass="org.simantics.form.model.adapters.FormLayoutAdapter" />
     </target>

 </adapters>

Each <target>...</target> section defines adaption to given interface.

<baseType> gives one or more types where all classes implementing the adaption have been inherited from.

<type> defines direct adaptation from instances of given type (uri) by construction the specified class (class) through Java Reflection API's whereas <adapter> defines adaptation from instances of given type (uri) by using the given adapter (adapterClass). The adapter has to implement the following interface:

 public interface Adapter<T> {
     void adapt(AsyncReadGraph g, Resource r, AsyncProcedure<T> procedure);
 }

In addition the adapter may contain this kind of definitions:

 <resource uri="http://www.simantics.org/Layer0-1.0/OrderedSetElements"
           class="org.simantics.layer0.utils.binaryPredicates.OrderedSetElementsPredicate"/>

This means that the resource defined by the uri is always adapted to an instance of the given class.

The main difference between <type>/<resource> definitions and <adapter> definitions is that <type>/<resource> adaptation cannot be used for complex adaptation since both can only be used to adapt to instances of the defined adapter class and nothing else. <adapter> on the other hand can perform more complex analysis and return any instance of the adaptation target interface.

Defined adapters can be used with the following synchronous methods, where clazz-parameter refers to the interface in the adapter declaration:

public interface ReadGraph {
    ....
    <T> T adapt(Resource resource, Class<T> clazz) throws AdaptionException, ValidationException, ServiceException;
    <T> T adaptUnique(Resource resource, Class<T> clazz) throws AdaptionException, ValidationException, ServiceException;
    <T> T getPossibleAdapter(Resource resource, Class<T> clazz) throws ValidationException, ServiceException;
    <T> T getPossibleUniqueAdapter(Resource resource, Class<T> clazz) throws ValidationException, ServiceException;
    ...
}

For details, see ReadGraph interface.

In addition to <adapter>-elements, the file may contain <installer>-elements, with class-attribute referring to an implementation of the interface:

public interface AdapterInstaller {
    void install(ReadGraph g, AdaptionService service) throws Exception;
}

where

public interface AdaptionService {
    <T> void adapt(AsyncReadGraph g, Resource r, Class<T> clazz, boolean possible, AsyncProcedure<T> procedure);
    <T> void adaptNew(AsyncReadGraph g, Resource r, Class<T> clazz, boolean possible, AsyncProcedure<T> procedure);
    <T> void declareAdapter(Resource type, Class<T> clazz);
    <T> void addAdapter(Resource type, Class<T> clazz, Adapter<T> adapter);
    <T> void addInstanceAdapter(Resource resource, Class<T> clazz, Adapter<T> adapter);    
}

The class installs adapters by using method addAdapter (and addInstanceAdapter). You should use the xml-file to define your adapters if possible. The installer-mechanism is provided for cases where you want to install an adapter that is constructed with special parameters.

Adapter Parameters

Certain parameters can be specified for adapters defined with <type> and <resource> elements. These arguments will be directly forwarded to a constructor matching this argument list in your adapter implementation class.

<this/>

The resource that is being adapted.

<graph/>

ReadGraph for reading the graph database during construction of the adapted class instance.

<bundle/>

Bundle class instance which identifies the bundle in which this adapter is defined. Can also be used to specify the referenced bundle explicitly using its symbolic name, e.g. <bundle>bundle.id</bundle>.

<string>FOO</string>

Any string constant contained as text within the <string> tag. Passed to the constructor as a java.lang.String instance.

<single/>

Requires attribute uri which must specify a URI to a relation resource. If adaption is performed for resource S, this argument element looks for a single object O from the statement (S, R, O) and passes O as an argument to the adapter class. If no single object O exists, the adapter is given null.

<related/>

Requires attribute uri which must specify a URI to a relation resource. If adaption is performed for resource S, this argument element looks for all objects O existing in the database (S, R, O) and the objects to the adapter as a Collection<Resource>.

<orderedSet/>

Requires attribute uri which must specify a URI to an ordered set resource. Looks for the specified URI in the database, assumes it is an ordered set resource and loads the whole ordered set into a Collection<Resource> instance. This collection is passed as an argument to the adapter class.