Projects



HowTo: Concept Diagrams and Ontology Generation with FS Concepts

This document explains the purpose and handling of the Concept Daigrams provided by the plugins: Feature Structures (Renew plugin FS) and Feature Structure Ontology Generator (Mulan plugin FSOntologyGenerator).

(1) Introduction

The FSOntologyGenerator plugin is used to build the ontology for a Mulan project from simple Renew drawings. Hereby, it replaces the Protégé-based OntologyGenerator plugin. The generated classes then encapsulate a FIPA-compatible ontology with convenient constructors and accessors and the build-in functionality to parse or generate ACL message strings with SL representations for the concepts.

As a reference, the Mulan plugin FSOntologyTestPlugin is a complete example project showing how the generator could be used.

(2) Embedment in the PAOSE Development Process

The following figure shows how the !FSOntologyGenerator plugin fits into the overall PAOSE development process.

Classification of Ontology (and Generation) in Paose

There are two development areas where the ontology plays a role:

  • The ontology design, which is done using concept diagrams
  • The implementation of nets which make use of the generated classes, especially protocol nets.

(3) Creating Ontology Diagrams

Example ontology created as Concept Diagram

An ontology is defined by a concept diagrams, which is very similar to UML class diagrams. They are simply stored as .rnw files.

(3.1) XFS Toolbar components

Ontology diagrams are built using the Renew XFS toolbar (you get it through Simulation -> Formalisms -> XFS Net Compiler).

Tools offered by the XFS formalism

Actually, just two of its tools are really needed:

  • Concept Tool: Used to add new concepts to the diagram. After clicking on the drawing pane, you get a text box where you can enter the concept name and its slots (if it has some)
  • Disjunctive Is-A Tool: Relates a concept to a superconcept. Slots will be inherited.

Don't use the other Is-A tool, as its semantics cannot be translated to class hierarchies, thus edges drawn with this will yield a compiler error.

(3.2) Declaring Slots

Usually, slot definitions for a concept are put into the concept box after the concept name (be sure to put two line breaks in between). The syntax is name:type, where type may be any Java type (primitive or class) or any concept name (in the same ontology or one that is imported or accessed (see 3.6). The type may optionally be followed by a Kleene star (*) to indicate a multi-valued slot which will be represented as a list of values.

Example:

my-concept

one-slot:int
another-slot:some-concept
all-strings:String*

This defines a concept named my-concept with three slots:

  • one-slot has the type int
  • another-slot with type some-concept
  • all-strings with the type "list of strings"

Optionally, the feature tool could be used for slot declarations, instead, similar to UML diagram - although the "inline" way seems more readable and is therefore recommended.

(3.3) Documenting Concepts and Slots

For documentation of the generated classes, Javadoc-style comments may be written before the concept (the comment should also precede a stereotype if present - see 3.4) as well as before each slot.

Example:

/** This is the doc for my concept. */
my-concept

/** Doc for one slot. */
one-slot:int
another-slot:some-concept
/** Doc for all strings */
all-strings:string*

The slot called "another-slot" has no comment in this example.

If you get any compilation errors with documented concepts, check the syntax: For example, currently, there must be a line break directly after "*/".

(3.4) Abstract and Concrete Classes

Due to Capa restrictions, all concepts that are superconcepts have to be abstract (the generated class cannot be instantiated). The generator will therefore treat all concepts with subconcepts as abstract and those without subconcepts (in the same ontology) as concrete. For forcing a concept to be abstract, the <<abstract>> stereotype is used, for example:

<<abstract>>
my-concept

my-slot:int

This is useful for cross-ontology references (see 3.5).

(3.5) Cross-Ontology-References

It is possible to inherit from concepts that are in other ontologies by drawing a "reference concept" named "referencedOntology::conceptName" (there is no need to repeat the slots or stereotype in the reference) and inherit from this "reference concept". There must be an access declaration for the referenced ontology (see 3.6).

When inheriting from a foreign concept that has no "local subconcepts" (i.e. in the same ontology), the concept must be declared abstract (see 3.4).

(3.6) The Declaration Node

The declaration node holds ontology-level information like package, imports and accesses.

Package

The first line must declare the Java package of the ontology, just like in a Java source file.

Access Declarations

The next (zero to n) lines can be access declarations for referencing external ontologies. Be sure to configure the ant task right (see 4.2) so that the net holding the referenced ontology can be found. An access declaration consists of the "access" keyword as well as the name of the referenced ontology (but without the package). After accessing an ontology, all of its concepts can be used as slot types and for inheritence (see 3.5).

Import Declarations

Following the access declarations, there can be any number of normal java imports. All imported classes can be used as slot types (but not for inheritence).

Be sure to put package, accesses and imports in the order given here - otherwise a parser error will occur.

Example:

package de.renew.agent.settler.ontology;
access Basic;
access GameConcepts;
import java.net.URI;
import java.util.*;

(4) Configuration of the Ant Target

General information about ant can be found at http://ant.apache.org/manual/. The basics are axplained pretty well at http://ant.apache.org/manual/using.html.

To call the ontology generator from ant, there is the createfsontology task.

(4.1) Basic Functionality

In the simple case, it takes a destination directory as (mandatory) parameter. A fileset of ontology-sns-files belongs in the body - all those ontologies will be generated.

Simple example:

  <createfsontology destdir="build/gensrc">
    <fileset dir="build/classes/ontology">
      <include name="*.sns"/>
    </fileset>
  </createfsontology> 

Note that using explicit paths like here is considered bad style - usually, it is a better idea to use properties for directory references, which will be done in the following examples.

(4.2) The Netpath

The netpath is needed in order to find ontologies referenced by access declarations (see 3.6). All ontologies in the body-fileset will automatically be added to the netpath, so it is no problem to access one generated ontology from another.

But sometimes, there are ontologies in other plugins or target folders that we would like to refererence. In this case, we need to use a reference to a netpath:

  <createfsontology destdir="${dir.build.gensrc}" netpathref="ref.ontology.netpath">
    <fileset dir="${dir.build.classes}">
      <include name="ontology/ProdStoreCons.sns"/>
    </fileset>
  </createfsontology>   	

Now, all references from the ProdStoreCons ontology to ontologies found in ref.ontology.netpath will be correctly resolved. The netpath could have been configured like this:

  <path id="ref.ontology.netpath">  	      
    <pathelement location="${dir.build.classes}/ontology"/>
  </path>

(4.3) Where the Shadows Come From

Shadow nets are usually built from the .rnw source using the createsns task. You should use a seperate targets for building the ontology sns files and the reference net sns files, instead of trying to do all in the same target. This is because for syntax check, the createsns task should be set up to compile the nets. Then, we should use the XFSNetCompiler instead of the JavaNetCompiler. Also, there would be cyclic references: The ontology generator needs the sns target, while the reference nets need the generated ontologies to compile.

The ontology sns target might look like this:

<target name="ontology.sns.app" depends="init">
  <path id="ref.classpath.sns">
    <path refid="ref.classpath"/>
    <pathelement location="${dir.build.classes}"/>
  </path>

  <!-- creates separate sns for each rnw -->
  <createsns destdir="${dir.build.classes}" 
             classpathref="ref.classpath.sns"
             compilername="de.renew.formalism.fsnet.XFSNetCompiler" 
	     compile="true">
    <fileset dir="${dir.src}">	      	
      <include name="ontology/**/*.rnw"/>
    </fileset>
  </createsns>
</target>

(4.4) Full example from FSOntologyTestPlugin

This example shows the complete ontology.app target from FSOntologyTestPlugin. There are two ontologies: Basic and ProdStoreCons. They will be built in two phases for demonstration, although they are in the same plugin and folder - usually, of course, one would just do both at once.

<target name="ontology.app" depends="init.mulantasks,ontology.sns.app">
  <!-- Build the ontology in two phases just to demonstrate how it works -->
  <!-- First, just the "Basic" ontology, no netpath is needed here -->
  <createfsontology destdir="${dir.build.gensrc}">
    <fileset dir="${dir.build.classes}">
      <include name="ontology/Basic.sns"/>
    </fileset>
  </createfsontology> 

  <!-- Configure the netpath to look for referenced ontology sns's -->
  <path id="ref.ontology.netpath">  	      
    <pathelement location="${dir.build.classes}/ontology"/>
  </path>

  <!-- Second, the ProdStoreCons ontology which references Basic.
       As Basic is not in the fileset, we need the netpath here --> 
  <createfsontology destdir="${dir.build.gensrc}" netpathref="ref.ontology.netpath">
    <fileset dir="${dir.build.classes}">
      <include name="ontology/ProdStoreCons.sns"/>
    </fileset>
  </createfsontology>   	
</target>

(5) About the Generated Classes

For each ontology concept, a Java class is being generated. Abstract/concrete concepts will have abstract/concrete classes, respectively.

(5.1) Class Hierarchy

The generated class hierarchy normaly reflects the concept hierarchy - with a few exceptions:

  • The superconcept of all concepts is "concept", theoretically - but this has no semantic meaning, so a concept with this name will just be ignored for the generation process.
  • Concepts with name "agent-action" or "predicate" will not be generated, either, but concepts inheriting from them will be regarded agent actions or predicate, respectively. Classes generated from agent actions and predicates will have special methods (see 5.5).
  • Concepts directly inheriting from "predicate" are mapped to classes inheriting from GenericVT, while all other concepts with no superconcepts besides "concept" or "agent action" inherit from GenericKVT. This is due to FIPA specifications, where predicated are regarded as value-tuples, while other concepts are usually key-value-tuples.
  • Sometimes people feel happy to draw the concept agent-identifier - although the corresponding class is already implemented in Capa. Therefore, this concept will also be ignore by the generator.

(5.1) Automatic Name Conversion

All concept names, slot types and slot names will be converted to Java conventions using the following two rules:

  • Hyphens will be replaced by camelCase.
  • Concept names and non-primitive slot types will be capitalized to yield legal class names.

For example, the concept defined by

my-nice-concept

first-slot:int
second-slot:String
third-slot:neighbour-concept

generates a class of name MyNiceConcept with three slots:

  • firstSlot of type int
  • secondSlot of type String
  • thirdSlot of type NeighbourConcept

(5.2) Constructors

Concrete classes have two visible constructors:

  • A basic constructor without any parameters - all slots will be null unless explicitly set later
  • A conveniance constructor with a parameter for each slot. If null is given instead of any parameter, the corresponding slot will not be set. Multi-valued slots have parameter type VTSequence. Primitive types slots have the boxed type as parameter (e.g. int has Integer), so null can also be used here.

Also, the static methods fromString and fromAclMessage(for predicates, this is called predicateFromAclMessage) are generated, which take a string in SL1 and Acl format, respectively, which will be parsed. They then return an instance of the concept class. (It was not possible to make this a constructor, as a concept with exactly one slot of type String already has a conveniance constructor with this signature.)

(5.3) Accessors for Simple Slots

Simple (that is, not multi-valued) slots have simply a getter and a setter using the types given in the slot definition. This also holds for primitive Java types like int, although slots of this kind are internally stored using the boxed types - the conversion is done automatically by the accessors.

(5.4) Accessors for Multi-Valued Slots

For multi-valued slots or list slots, several methods will be generated. For example, the slot defined by

village:String*

produces the following methods:

  • void setVillage(VTSequence vals) Sets the slot to the given list.
  • VTSequence getAllVillage() Gets all villages stored in the list slot.
  • Iterator getAllVillageAsIterator() Gives an iterator over all villages.
  • void addVillage(String val) Adds a village to the list.
  • void removeVillage(String val) Removes the given village from the list.
  • void addAllVillage(VTSequence vals) Appends the given list of villages.
  • void removeAllVillage(VTSequence vals) Removes all villages in the given list.

Automatic boxing is also done for list slots, so a slot of type int* will have add- and remove-methods with type int as parameter.

(5.5) Other Generated Methods

As mentioned in 5.1, agent actions and predicates will get a few extra methods generated.

Methods for agent actions:

  • toActionRequest(AgentIdentifier receiver) Generates an Acl message which asks the receiver to perform this action.
  • Depricated:
    • toAclMessage(AgentIdentifier receiver) Alias for toActionRequest.
    • toAclInform(AgentIdentifier receiver) Wraps an inform around the action. This construct should rather be avoided, as it is semantically unclear who is performing the action.

Methods for predicates:

  • toSimpleInform(AgentIdentifier receiver) Wraps an inform message around this predicate.
  • toQueryIf(AgentIdentifier receiver) Wraps a query-if message around this predicate.
Last modified 13 years ago Last modified on Nov 1, 2010, 10:37:33 AM

Attachments (3)

Download all attachments as: .zip