diff --git a/org.eventb.texteditor.feature/.project b/org.eventb.texteditor.feature/.project
new file mode 100644
index 0000000000000000000000000000000000000000..397bf70082bb74a5c6494a551f1510adbced1bfb
--- /dev/null
+++ b/org.eventb.texteditor.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eventb.texteditor.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eventb.texteditor.feature/build.properties b/org.eventb.texteditor.feature/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..64f93a9f0b7328eb563aa5ad6cec7f828020e124
--- /dev/null
+++ b/org.eventb.texteditor.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/org.eventb.texteditor.feature/compile.org.eventb.texteditor.feature.xml b/org.eventb.texteditor.feature/compile.org.eventb.texteditor.feature.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ff68285d8f19c3fad2dbaa2fbeb69cff6341240e
--- /dev/null
+++ b/org.eventb.texteditor.feature/compile.org.eventb.texteditor.feature.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="Compile org.eventb.texteditor.feature" default="main">
+	<target name="main">
+		<ant antfile="build.xml" dir="../org.eventb.texttools" target="build.jars"/>
+		<ant antfile="build.xml" dir="../org.eventb.texteditor.ui" target="build.jars"/>
+	</target>
+</project>
diff --git a/org.eventb.texteditor.feature/feature.xml b/org.eventb.texteditor.feature/feature.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3fb82a3cdec61fd6a390105dac9d4f6ffe76c328
--- /dev/null
+++ b/org.eventb.texteditor.feature/feature.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+      id="org.eventb.texteditor.feature"
+      label="Camille TextEditor"
+      version="1.1.2.beta"
+      provider-name="Heinrich-Heine University Dusseldorf"
+      plugin="org.eventb.texteditor.ui">
+
+   <description>
+      A text editor for the Rodin platform to edit Event-B models
+-----------------------------------------------------------
+Release History:
+1.0.2 - Rodin 1.1 release
+1.0.1 - Branding, Relaxed version restrictions
+1.0.0 - Initial relase
+   </description>
+
+   <copyright>
+      Copyright (c) 2009 Heinrich-Heine University Dusseldorf.
+All rights reserved.
+   </copyright>
+
+   <license>
+      RODIN SOFTWARE USER AGREEMENT  
+June 1, 2006  
+Usage Of Content  
+THE RODIN PROJECT MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION
+AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY
+&quot;CONTENT&quot;).  USE OF THE CONTENT IS GOVERNED BY THE TERMS AND
+CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS
+OF  
+LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW.
+BY USING  
+THE CONTENT, YOU AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED
+BY  
+THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE
+LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW.
+IF YOU  
+DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND
+THE  
+TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
+NOTICES  
+INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.
+Applicable Licenses  
+Unless otherwise indicated, all Content made available by the
+Rodin  
+Project is provided to you under the terms and conditions of
+the  
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL
+is  
+provided with this Content and is also available at  
+http://www.eclipse.org/legal/epl-v10.html.  For purposes of the
+EPL,  
+&quot;Program&quot; will mean the Content.  
+Content includes, but is not limited to, source code, object
+code,  
+documentation and other files maintained in the Rodin SourceForge
+CVS  
+repository (&quot;Repository&quot;) in CVS modules (&quot;Modules&quot;) and made
+available as downloadable archives (&quot;Downloads&quot;).  
+- Content may be structured and packaged into modules to facilitate
+delivering, extending, and upgrading the Content.  Typical modules
+may include plug-ins (&quot;Plug-ins&quot;), plug-in fragments  
+(&quot;Fragments&quot;), and features (&quot;Features&quot;).  
+- Each Plug-in or Fragment may be packaged as a sub-directory
+or JAR  
+(Java(TM) ARchive) in a directory named &quot;plugins&quot;.  
+- A Feature is a bundle of one or more Plug-ins and/or Fragments
+and  
+associated material.  Each Feature may be packaged as a  
+sub-directory in a directory named &quot;features&quot;.  Within a Feature,
+files named &quot;feature.xml&quot; may contain a list of the names and
+version numbers of the Plug-ins and/or Fragments associated with
+that Feature.  
+- Features may also include other Features (&quot;Included  
+Features&quot;). Within a Feature, files named &quot;feature.xml&quot; may 
+contain a list of the names and version numbers of Included 
+Features.  
+The terms and conditions governing Plug-ins and Fragments should
+be  
+contained in files named &quot;about.html&quot; (&quot;Abouts&quot;). The terms and
+conditions governing Features and Included Features should be
+contained in files named &quot;license.html&quot; (&quot;Feature Licenses&quot;).
+Abouts  
+and Feature Licenses may be located in any directory of a Download
+or  
+Module including, but not limited to the following locations:
+- The top-level (root) directory  
+- Plug-in and Fragment directories  
+- Inside Plug-ins and Fragments packaged as JARs  
+- Sub-directories of the directory named &quot;src&quot; of certain Plug-ins
+- Feature directories  
+Note: if a Feature made available by the Rodin Project is installed
+using the Eclipse Update Manager, you must agree to a license
+(&quot;Feature Update License&quot;) during the installation process. 
+If the  
+Feature contains Included Features, the Feature Update License
+should  
+either provide you with the terms and conditions governing the
+Included Features or inform you where you can locate them.  Feature
+Update Licenses may be found in the &quot;license&quot; property of files
+named  
+&quot;feature.properties&quot; found within a Feature.  Such Abouts, Feature
+Licenses, and Feature Update Licenses contain the terms and conditions
+(or references to such terms and conditions) that govern your
+use of  
+the associated Content in that directory.  
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY
+REFER TO  
+THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.
+SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT
+LIMITED TO):  
+- Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)
+- Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)
+- Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)
+- IBM Public License 1.0 (available at http://oss.software.ibm.com/developerworks/opensource/license10.html)
+- Metro Link Public License 1.00 (available at http://www.opengroup.org/openmotif/supporters/metrolink/license.html)
+- Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS
+PRIOR TO USE OF THE CONTENT.  If no About, Feature License, or
+Feature  
+Update License is provided, please contact the Rodin Project
+to  
+determine what terms and conditions govern that particular  
+Content.  
+Cryptography  
+Content may contain encryption software. The country in which
+you are  
+currently may have restrictions on the import, possession, and
+use,  
+and/or re-export to another country, of encryption software.
+BEFORE  
+using any encryption software, please check the country&apos;s laws,
+regulations and policies concerning the import, possession, or
+use,  
+and re-export of encryption software, to see if this is permitted.
+Java and all Java-based trademarks are trademarks of Sun Microsystems,
+Inc. in the United States, other countries, or both.
+   </license>
+
+   <url>
+      <update label="Heinrich-Heine University Düsseldorf" url="http://www.stups.uni-duesseldorf.de/update/"/>
+      <discovery label="Rodin Update Site" url="http://rodin-b-sharp.sourceforge.net/updates"/>
+   </url>
+
+   <requires>
+      <import plugin="org.eventb.ui" version="1.2.0" match="equivalent"/>
+      <import plugin="org.eclipse.ui.editors" version="3.5.0" match="equivalent"/>
+      <import plugin="org.eclipse.jface.text" version="3.5.0" match="equivalent"/>
+      <import plugin="org.eclipse.emf.edit.ui" version="2.5.0" match="equivalent"/>
+      <import plugin="org.eventb.eventBKeyboard" version="2.9.0" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.core.runtime" version="3.5.0" match="equivalent"/>
+      <import plugin="org.eventb.core.ast" version="1.2.0" match="equivalent"/>
+      <import plugin="org.rodinp.core" version="1.2.0" match="equivalent"/>
+      <import plugin="org.eclipse.emf.compare" version="1.0.0" match="compatible"/>
+      <import plugin="org.eclipse.emf.compare.diff" version="1.0.0" match="compatible"/>
+      <import plugin="org.eclipse.emf.compare.match" version="1.0.0" match="compatible"/>
+      <import feature="org.eventb.emf.feature" version="1.2.1" match="compatible"/>
+   </requires>
+
+   <plugin
+         id="org.eventb.texteditor.ui"
+         download-size="0"
+         install-size="0"
+         version="1.1.1"
+         unpack="false"/>
+
+   <plugin
+         id="org.eventb.texttools"
+         download-size="0"
+         install-size="0"
+         version="1.1.2"
+         unpack="false"/>
+
+</feature>
diff --git a/org.eventb.texteditor.ui/.classpath b/org.eventb.texteditor.ui/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..cdf77e53eb0c88a3e3b0dba86a5a4f67137572a6
--- /dev/null
+++ b/org.eventb.texteditor.ui/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="lib/commons-lang-2.4.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eventb.texteditor.ui/.project b/org.eventb.texteditor.ui/.project
new file mode 100644
index 0000000000000000000000000000000000000000..f4c75c1f6b72a7280918a29c18c2623a122b7153
--- /dev/null
+++ b/org.eventb.texteditor.ui/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eventb.texteditor.ui</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eventb.texteditor.ui/.settings/org.eclipse.jdt.core.prefs b/org.eventb.texteditor.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..bb1930094c1824baf52a0d8ba07d7ba68c17472f
--- /dev/null
+++ b/org.eventb.texteditor.ui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Thu Mar 26 12:38:05 CET 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/org.eventb.texteditor.ui/META-INF/MANIFEST.MF b/org.eventb.texteditor.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000000000000000000000000000000000..ea8df4bfef9fb9cadb01f8e5a0aaa6779fed7e56
--- /dev/null
+++ b/org.eventb.texteditor.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Camille Texteditor
+Bundle-SymbolicName: org.eventb.texteditor.ui;singleton:=true
+Bundle-Version: 1.1.1
+Bundle-Localization: plugin
+Bundle-Activator: org.eventb.texteditor.ui.TextEditorPlugin$Implementation
+Require-Bundle: org.eventb.texttools;bundle-version="[1.1.0,1.2.0)";visibility:=reexport,
+ org.eventb.emf.formulas;bundle-version="[1.0.0,1.1.0)";visibility:=reexport,
+ org.eventb.emf.persistence;bundle-version="[1.1.1,1.2.0)";visibility:=reexport,
+ org.eventb.ui;bundle-version="[1.2.0,1.3.0)";visibility:=reexport,
+ org.eclipse.ui.editors;bundle-version="[3.5.0,3.6.0)";visibility:=reexport,
+ org.eclipse.jface.text;bundle-version="[3.5.0,3.6.0)";visibility:=reexport,
+ org.eclipse.emf.edit.ui;bundle-version="[2.5.0,2.6.0)";visibility:=reexport,
+ org.eventb.eventBKeyboard;bundle-version="[2.9.0,3.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ClassPath: lib/commons-lang-2.4.jar,
+ .
+Bundle-Vendor: Heinrich-Heine University Dusseldorf
+Export-Package: org.eventb.texteditor.ui,
+ org.eventb.texteditor.ui.editor
diff --git a/org.eventb.texteditor.ui/about.ini b/org.eventb.texteditor.ui/about.ini
new file mode 100644
index 0000000000000000000000000000000000000000..b0f2a9ba8dfb89d90a2a3eb5ddfc7abf30e341a3
--- /dev/null
+++ b/org.eventb.texteditor.ui/about.ini
@@ -0,0 +1 @@
+featureImage=icons/camille32.gif
\ No newline at end of file
diff --git a/org.eventb.texteditor.ui/build.properties b/org.eventb.texteditor.ui/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..2d2dfd6f245d8d6bc422769491b488641cc063d3
--- /dev/null
+++ b/org.eventb.texteditor.ui/build.properties
@@ -0,0 +1,20 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               lib/commons-lang-2.4.jar,\
+               plugin.properties,\
+               templates.xml,\
+               icons/
+src.includes = templates.xml,\
+               src/,\
+               plugin.xml,\
+               plugin.properties,\
+               build.properties,\
+               META-INF/,\
+               .project,\
+               .classpath,\
+               .settings/,\
+               lib/,\
+               icons/
diff --git a/org.eventb.texteditor.ui/icons/camille16.gif b/org.eventb.texteditor.ui/icons/camille16.gif
new file mode 100644
index 0000000000000000000000000000000000000000..aa4659863cf52ec7742b3afc93b1cdefb4236c9f
Binary files /dev/null and b/org.eventb.texteditor.ui/icons/camille16.gif differ
diff --git a/org.eventb.texteditor.ui/icons/camille32.gif b/org.eventb.texteditor.ui/icons/camille32.gif
new file mode 100644
index 0000000000000000000000000000000000000000..3d6ec1dd8af370c87dcebb265fb46652d35b44c6
Binary files /dev/null and b/org.eventb.texteditor.ui/icons/camille32.gif differ
diff --git a/org.eventb.texteditor.ui/icons/camille64.gif b/org.eventb.texteditor.ui/icons/camille64.gif
new file mode 100644
index 0000000000000000000000000000000000000000..7807bd63c49d363ff13ef2b1901295d484286c07
Binary files /dev/null and b/org.eventb.texteditor.ui/icons/camille64.gif differ
diff --git a/org.eventb.texteditor.ui/icons/template_obj.gif b/org.eventb.texteditor.ui/icons/template_obj.gif
new file mode 100644
index 0000000000000000000000000000000000000000..fdde5fbb95e50851757a749194ce6a3431bb1afb
Binary files /dev/null and b/org.eventb.texteditor.ui/icons/template_obj.gif differ
diff --git a/org.eventb.texteditor.ui/plugin.properties b/org.eventb.texteditor.ui/plugin.properties
new file mode 100644
index 0000000000000000000000000000000000000000..6da1d5fb6f25c8562e01d5e63e4f5dfb5b06820d
--- /dev/null
+++ b/org.eventb.texteditor.ui/plugin.properties
@@ -0,0 +1,60 @@
+
+# <copyright>
+# </copyright>
+#
+# $Id$
+
+pluginName = Camille Texteditor
+
+_UI_CoreEditor_menu = &Core Editor
+_UI_MachineEditor_menu = &Machine Editor
+_UI_ContextEditor_menu = &Context Editor
+
+_UI_CreateChild_menu_item = &New Child
+_UI_CreateSibling_menu_item = N&ew Sibling
+
+_UI_ShowPropertiesView_menu_item = Show &Properties View
+_UI_RefreshViewer_menu_item = &Refresh
+
+_UI_SelectionPage_label = Selection
+_UI_ParentPage_label = Parent
+_UI_ListPage_label = List
+_UI_TreePage_label = Tree
+_UI_TablePage_label = Table
+_UI_TreeWithColumnsPage_label = Tree with Columns
+_UI_ObjectColumn_label = Object
+_UI_SelfColumn_label = Self
+
+_UI_NoObjectSelected = Selected Nothing
+_UI_SingleObjectSelected = Selected Object: {0}
+_UI_MultiObjectSelected = Selected {0} Objects
+
+_UI_OpenEditorError_label = Open Editor
+
+_UI_Wizard_category = Example EMF Model Creation Wizards
+
+_UI_CreateModelError_message = Problems encountered in file "{0}"
+
+_UI_MachineModelWizard_label = Machine Model
+_UI_MachineModelWizard_description = Create a new Machine model
+_UI_MachineEditorFilenameDefaultBase = Machine
+_UI_MachineEditorFilenameExtensions = bum
+
+_UI_MachineEditor_label = Camille Texteditor
+
+_UI_Wizard_label = New
+
+_WARN_FilenameExtension = The file name must end in ''.{0}''
+_WARN_FilenameExtensions = The file name must have one of the following extensions: {0}
+
+_UI_ModelObject = &Model Object
+_UI_XMLEncoding = &XML Encoding
+_UI_XMLEncodingChoices = UTF-8 ASCII UTF-16 UTF-16BE UTF-16LE ISO-8859-1
+_UI_Wizard_initial_object_description = Select a model object to create
+
+_UI_FileConflict_label = File Conflict
+_WARN_FileConflict = There are unsaved changes that conflict with changes made outside the editor.  Do you wish to discard this editor's changes?
+
+ContentAssistProposal.label=Content assist
+ContentAssistProposal.tooltip=Content assist
+ContentAssistProposal.description=Provides Content Assistance
diff --git a/org.eventb.texteditor.ui/plugin.xml b/org.eventb.texteditor.ui/plugin.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8b34c6c4e2e2fad0572c3fe7038a14ecee178dc8
--- /dev/null
+++ b/org.eventb.texteditor.ui/plugin.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+   <extension
+         id="eventb_texteditor"
+         name="EventB Texteditor"
+         point="org.eclipse.ui.editors">
+      <editor
+            class="org.eventb.texteditor.ui.editor.EventBTextEditor"
+            default="true"
+            id="org.eventb.texteditor.ui.texteditor"
+            name="Camille Text Editor">
+         <contentTypeBinding
+               contentTypeId="org.eventb.core.machineFile">
+         </contentTypeBinding>
+         <contentTypeBinding
+               contentTypeId="org.eventb.core.contextFile">
+         </contentTypeBinding>
+      </editor>
+   </extension>
+   <extension
+         id="syntaxerror"
+         name="Syntax Error"
+         point="org.eclipse.core.resources.markers">
+      <super
+            type="org.eclipse.core.resources.problemmarker">
+      </super>
+   </extension>
+   <extension
+         id="eventb.templates"
+         name="EventB TextEditor Templates"
+         point="org.eclipse.ui.editors.templates">
+      <contextType
+            class="org.eclipse.jface.text.templates.TemplateContextType"
+            id="org.eventb.texteditor.machine"
+            name="Machine Context">
+      </contextType>
+      <contextType
+            class="org.eclipse.jface.text.templates.TemplateContextType"
+            id="org.eventb.texteditor.events"
+            name="Events Context">
+      </contextType>
+      <contextType
+            class="org.eclipse.jface.text.templates.TemplateContextType"
+            id="org.eventb.texteditor.context"
+            name="Context Context">
+      </contextType>
+      <contextType
+            class="org.eclipse.jface.text.templates.TemplateContextType"
+            id="org.eventb.texteditor.formula"
+            name="Formula Context">
+      </contextType>
+      <contextType
+            class="org.eclipse.jface.text.templates.TemplateContextType"
+            id="org.eventb.texteditor.anywhere"
+            name="Anywhere Context">
+      </contextType>
+      <include
+            file="templates.xml">
+      </include>
+   </extension>
+   <extension
+         point="org.eclipse.ui.preferencePages">
+      <page
+            category="org.eventb.texteditor.ui.preferences"
+            class="org.eventb.texteditor.ui.preferences.HighlightingPreferencePage"
+            id="org.eventb.texteditor.ui.preferences.HighlightingPreferencePage"
+            name="Syntax Highlighting">
+      </page>
+      <page
+            category="org.eventb.ui.preferences.eventB"
+            class="org.eventb.texteditor.ui.preferences.TextEditorPreferencePage"
+            id="org.eventb.texteditor.ui.preferences"
+            name="Camille TextEditor">
+      </page>
+      <page
+            category="org.eventb.texteditor.ui.preferences"
+            class="org.eventb.texteditor.ui.preferences.TemplatePrefPage"
+            id="org.eventb.texteditor.ui.preferences.TemplatePreferences"
+            name="Templates">
+      </page>
+   </extension>
+   <extension
+         point="org.eclipse.core.runtime.preferences">
+      <initializer
+            class="org.eventb.texteditor.ui.preferences.PreferenceInitializer">
+      </initializer>
+   </extension>
+   <extension
+         point="org.eclipse.ui.commands">
+      <command
+            defaultHandler="org.eventb.texteditor.ui.editor.actions.FormatHandler"
+            description="Formats the Event-B text representation"
+            id="org.eventb.texteditor.command.format"
+            name="Format">
+      </command>
+   </extension>
+   <extension point="org.eclipse.ui.menus">
+    <menuContribution locationURI="popup:#texteditor.context.menu?after=additions">
+        <command
+              commandId="org.eventb.texteditor.command.format"
+              label="Format"
+              mnemonic="F"
+              tooltip="Reformats the Event-B text representation with the default formatting">
+        </command>
+     </menuContribution>
+   </extension>
+   <extension
+         point="org.eclipse.ui.bindings">
+      <key
+            commandId="org.eventb.texteditor.command.format"
+            schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+            sequence="M1+M2+F">
+      </key>
+   </extension>
+   <extension point="org.eclipse.ui.handlers">
+      <handler
+            class="org.eventb.texteditor.ui.editor.actions.InsertHandler"
+            commandId="org.eventb.ui.edit.insert">
+          <activeWhen>
+             <with
+                   variable="activeEditorId">
+                   <equals
+                         value="org.eventb.texteditor.ui.texteditor">
+                   </equals>
+             </with>
+          </activeWhen>
+      </handler>
+   </extension>
+   
+</plugin>
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/Images.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/Images.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d0f77d354a28ae1322cd421b8e2a4c01b59272f
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/Images.java
@@ -0,0 +1,125 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.eventb.ui.EventBUIPlugin;
+
+public class Images {
+	public static final String IMG_MACHINE = "Machine";
+	public static final String IMG_CONTEXT = "Context";
+	public static final String IMG_REFINES = "Refines";
+	public static final String IMG_INVARIANT = "Invariant";
+	public static final String IMG_THEOREM = "Theorem";
+	public static final String IMG_VARIABLE = "Variable";
+	public static final String IMG_EVENT = "Event";
+	public static final String IMG_GUARD = "Guard";
+	public static final String IMG_ACTION = "Action";
+	public static final String IMG_AXIOM = "Axiom";
+	public static final String IMG_CONSTANT = "Constant";
+	public static final String IMG_CARRIER_SET = "Carrier Set";
+	public static final String IMG_REFINE_OVERLAY = IMG_REFINES + "Overlay";
+	public static final String IMG_COMMENT_OVERLAY = "CommentOverlay";
+	public static final String IMG_WARNING_OVERLAY = "WarningOverlay";
+	public static final String IMG_ERROR_OVERLAY = "ErrorOverlay";
+
+	private static final String IMG_MACHINE_PATH = "icons/full/obj16/mch_obj.gif";
+	private static final String IMG_CONTEXT_PATH = "icons/full/obj16/ctx_obj.gif";
+	private static final String IMG_REFINES_PATH = "icons/full/ctool16/refines.gif";
+	private static final String IMG_INVARIANT_PATH = "icons/full/obj16/inv_obj.gif";
+	private static final String IMG_THEOREM_PATH = "icons/full/obj16/thm_obj.gif";
+	private static final String IMG_VARIABLE_PATH = "icons/full/obj16/var_obj.gif";
+	private static final String IMG_EVENT_PATH = "icons/full/obj16/evt_obj.gif";
+	private static final String IMG_GUARD_PATH = "icons/full/obj16/grd_obj.gif";
+	// private static final String IMG_WITNESS_PATH =
+	// "icons/full/obj16/evt_obj.gif";
+	private static final String IMG_ACTION_PATH = "icons/full/obj16/act_obj.gif";
+	private static final String IMG_AXIOM_PATH = "icons/full/obj16/axm_obj.gif";
+	private static final String IMG_CONSTANT_PATH = "icons/full/obj16/cst_obj.gif";
+	private static final String IMG_CARRIER_SET_PATH = "icons/full/obj16/set_obj.gif";
+	private static final String IMG_REFINE_OVERLAY_PATH = "icons/full/ovr16/ref_ovr.gif";
+	private static final String IMG_COMMENT_OVERLAY_PATH = "icons/full/ovr16/cmt_ovr.gif";
+	private static final String IMG_WARNING_OVERLAY_PATH = "icons/full/ovr16/warning_ovr.gif";
+	private static final String IMG_ERROR_OVERLAY_PATH = "icons/full/ovr16/error_ovr.gif";
+
+	private static ImageRegistry imageRegistry = TextEditorPlugin.getPlugin()
+			.getImageRegistry();
+	private static Map<String, String> rodinIcons = new HashMap<String, String>();
+
+	static {
+		rodinIcons.put(IMG_MACHINE, IMG_MACHINE_PATH);
+		rodinIcons.put(IMG_CONTEXT, IMG_CONTEXT_PATH);
+		rodinIcons.put(IMG_REFINES, IMG_REFINES_PATH);
+		rodinIcons.put(IMG_INVARIANT, IMG_INVARIANT_PATH);
+		rodinIcons.put(IMG_THEOREM, IMG_THEOREM_PATH);
+		rodinIcons.put(IMG_VARIABLE, IMG_VARIABLE_PATH);
+		rodinIcons.put(IMG_EVENT, IMG_EVENT_PATH);
+		rodinIcons.put(IMG_GUARD, IMG_GUARD_PATH);
+		rodinIcons.put(IMG_ACTION, IMG_ACTION_PATH);
+		rodinIcons.put(IMG_AXIOM, IMG_AXIOM_PATH);
+		rodinIcons.put(IMG_CONSTANT, IMG_CONSTANT_PATH);
+		rodinIcons.put(IMG_CARRIER_SET, IMG_CARRIER_SET_PATH);
+
+		rodinIcons.put(IMG_REFINE_OVERLAY, IMG_REFINE_OVERLAY_PATH);
+		rodinIcons.put(IMG_COMMENT_OVERLAY, IMG_COMMENT_OVERLAY_PATH);
+		rodinIcons.put(IMG_WARNING_OVERLAY, IMG_WARNING_OVERLAY_PATH);
+		rodinIcons.put(IMG_ERROR_OVERLAY, IMG_ERROR_OVERLAY_PATH);
+	}
+
+	public static final String IMG_TEMPLATE = "Template";
+	public static final String IMG_TEMPLATE_PATH = "icons/template_obj.gif";
+
+	public static Image getImage(final String key) {
+		final Image image = imageRegistry.get(key);
+
+		if (image == null && rodinIcons.containsKey(key)) {
+			return getImage(key, rodinIcons.get(key));
+		} else {
+			return image;
+		}
+	}
+
+	public static Image getImage(final String key, final String path) {
+		Image image = imageRegistry.get(key);
+
+		if (image == null) {
+			registerImage(key, path);
+			image = imageRegistry.get(key);
+		}
+
+		return image;
+	}
+
+	public static void registerImage(final String key, final String path) {
+		if (rodinIcons.containsKey(key)) {
+			registerImage(EventBUIPlugin.PLUGIN_ID, key, path);
+		} else {
+			registerImage(TextEditorPlugin.PLUGIN_ID, key, path);
+		}
+	}
+
+	private static void registerImage(final String pluginID, final String key,
+			final String path) {
+		final ImageDescriptor desc = AbstractUIPlugin
+				.imageDescriptorFromPlugin(pluginID, path);
+		imageRegistry.put(key, desc);
+	}
+
+	public static void dispose() {
+		// imageRegistry.remove(key);
+	}
+
+	public static ImageRegistry getRegistry() {
+		return imageRegistry;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/MarkerHelper.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/MarkerHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..49eaae3075e039493e519affc44ee2a3087fda67
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/MarkerHelper.java
@@ -0,0 +1,81 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui;
+
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.edit.ui.EMFEditUIPlugin;
+import org.eclipse.emf.edit.ui.util.EditUIMarkerHelper;
+import org.eclipse.ui.texteditor.MarkerUtilities;
+import org.eventb.texteditor.ui.build.ast.IParseProblemWrapper;
+
+public class MarkerHelper extends EditUIMarkerHelper {
+
+	private final String markerId;
+
+	public MarkerHelper(final String markerId) {
+		this.markerId = markerId;
+
+	}
+
+	@Override
+	protected String getMarkerID() {
+		return markerId;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected boolean adjustMarker(final IMarker marker,
+			final Diagnostic diagnostic) throws CoreException {
+		if (diagnostic.getData() != null) {
+			for (final Object element : diagnostic.getData()) {
+				if (element instanceof IParseProblemWrapper) {
+					final IParseProblemWrapper parseProblem = (IParseProblemWrapper) element;
+					final int offset = parseProblem.getOffset();
+					final int line = parseProblem.getLine();
+					final int column = parseProblem.getColumn();
+
+					// Setting attributes for marker
+					final Map attributes = marker.getAttributes();
+
+					MarkerUtilities.setCharStart(attributes, offset);
+					MarkerUtilities.setCharEnd(attributes, offset
+							+ parseProblem.getTokenLength());
+					MarkerUtilities.setMessage(attributes, parseProblem
+							.getMessage());
+					MarkerUtilities.setLineNumber(attributes, line);
+
+					attributes.put(IMarker.LOCATION, EMFEditUIPlugin
+							.getPlugin().getString(
+									"_UI_MarkerLocation",
+									new String[] { Integer.toString(line + 1),
+											Integer.toString(column + 1) }));
+
+					marker.setAttributes(attributes);
+					return true;
+				}
+			}
+		}
+
+		return super.adjustMarker(marker, diagnostic);
+	}
+
+	public void deleteMarkers(final Resource resource, final String markerType,
+			final boolean includeSubtypes, final int depth) {
+		final IFile file = getFile(resource);
+		try {
+			file.deleteMarkers(markerType, includeSubtypes, depth);
+		} catch (final CoreException e) {
+			// IGNORE
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/TextDecoration.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/TextDecoration.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad35648038e0c2993eb19779a1b391533c799975
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/TextDecoration.java
@@ -0,0 +1,74 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+public class TextDecoration {
+
+	public enum ESyntaxElement {
+		Comment("Comment"), Label("Label"), Keyword("Keyword"), MathKeyword(
+				"Mathematical Keyword"), GlobalVariable("Global Variable"), Parameter(
+				"Event Parameter"), BoundedVariable("Bound Variable"), Constant(
+				"Constant"), Set("Carrier Set");
+
+		private final String label;
+		private final String colorKey;
+		private final String styleKey;
+		private final Token token = new Token(null);
+
+		private ESyntaxElement(final String label) {
+			this.label = label;
+
+			colorKey = toString() + "." + "colorPreference";
+			styleKey = toString() + "." + "stylePreference";
+		}
+
+		public String getLabel() {
+			return label;
+		}
+
+		public String getColorKey() {
+			return colorKey;
+		}
+
+		public String getStyleKey() {
+			return styleKey;
+		}
+
+		public synchronized Token getToken() {
+			if (token.getData() == null) {
+				token.setData(getAttribute(this));
+			}
+
+			return token;
+		}
+
+		public synchronized void resetToken() {
+			token.setData(getAttribute(this));
+		}
+	};
+
+	private final static IPreferenceStore store = TextEditorPlugin.getPlugin()
+			.getPreferenceStore();
+
+	private static TextAttribute getAttribute(final ESyntaxElement element) {
+		final RGB color = PreferenceConverter.getColor(store, element
+				.getColorKey());
+		final int style = store.getInt(element.getStyleKey());
+
+		final TextAttribute attribute = new TextAttribute(new Color(Display
+				.getDefault(), color), null, style);
+		return attribute;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/TextEditorPlugin.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/TextEditorPlugin.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c40c04988d1acfb191f2ac2c69a96f593fd5241
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/TextEditorPlugin.java
@@ -0,0 +1,165 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texteditor.ui;
+
+import java.io.IOException;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.EMFPlugin;
+import org.eclipse.emf.common.ui.EclipseUIPlugin;
+import org.eclipse.emf.common.util.ResourceLocator;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.editors.text.templates.ContributionContextTypeRegistry;
+import org.eclipse.ui.editors.text.templates.ContributionTemplateStore;
+import org.eventb.texteditor.ui.build.dom.DomManager;
+import org.eventb.texteditor.ui.editor.codecompletion.DefaultContentAssist.ContextType;
+
+public final class TextEditorPlugin extends EMFPlugin {
+	/**
+	 * Plug-in ID
+	 */
+	public static final String PLUGIN_ID = "org.eventb.texteditor.ui";
+
+	/**
+	 * ID to identify the texteditor
+	 */
+	public static final String TEXTEDITOR_ID = PLUGIN_ID + ".texteditor";
+
+	/**
+	 * ID for text editor context menu
+	 */
+	public static final String TEXTEDITOR_CONTEXTMENU_ID = "#texteditor.context.menu";
+
+	/**
+	 * ID for problem markers of this plugin
+	 */
+	public static final String SYNTAXERROR_MARKER_ID = PLUGIN_ID + ".syntaxerror";
+
+	/**
+	 * ID the 'format' command
+	 */
+	public static final String FORMAT_COMMAND_ID = "org.eventb.texteditor.command.format";
+
+	/**
+	 * Singleton instance of EMFPlugin
+	 */
+	public static final TextEditorPlugin INSTANCE = new TextEditorPlugin();
+
+	/**
+	 * Singleton instance of EclipseUIPlugin
+	 */
+	private static Implementation plugin;
+
+	private static final DomManager domManager = new DomManager();
+
+	public TextEditorPlugin() {
+		super(new ResourceLocator[] {});
+	}
+
+	@Override
+	public ResourceLocator getPluginResourceLocator() {
+		return plugin;
+	}
+
+	/**
+	 * Returns the singleton instance of the {@link EclipseUIPlugin} plugin.
+	 * 
+	 * @return the singleton instance.
+	 */
+	public static Implementation getPlugin() {
+		return plugin;
+	}
+
+	public static DomManager getDomManager() {
+		return domManager;
+	}
+
+	/**
+	 * This looks up a string in plugin.properties, making a substitution.
+	 */
+	public static String getString(final String key, final Object s1) {
+		return INSTANCE.getString(key, new Object[] { s1 });
+	}
+
+	/**
+	 * The actual implementation of the Eclipse <b>Plugin</b> (
+	 * {@link EclipseUIPlugin}).
+	 */
+	public static class Implementation extends EclipseUIPlugin {
+		private ResourceBundle resourceBundle;
+		private ContributionTemplateStore templateStore;
+		private ContributionContextTypeRegistry contextTypeRegistry;
+
+		public Implementation() {
+			super();
+			plugin = this;
+
+			try {
+				resourceBundle = ResourceBundle.getBundle("plugin");
+			} catch (final MissingResourceException e) {
+				log(new Status(IStatus.ERROR, PLUGIN_ID,
+						"Cannot load resource bundle", e));
+				resourceBundle = null;
+			}
+		}
+
+		public ResourceBundle getResourceBundle() {
+			return resourceBundle;
+		}
+
+		public IWorkbenchPage getActivePage() {
+			final IWorkbenchWindow window = getWorkbench()
+					.getActiveWorkbenchWindow();
+			if (window == null) {
+				return null;
+			}
+			return window.getActivePage();
+		}
+
+		public ContributionContextTypeRegistry getContextTypeRegistry() {
+			if (contextTypeRegistry == null) {
+				contextTypeRegistry = new ContributionContextTypeRegistry();
+				contextTypeRegistry.addContextType(ContextType.Anywhere.key);
+				contextTypeRegistry.addContextType(ContextType.Machine.key);
+				contextTypeRegistry.addContextType(ContextType.Events.key);
+				contextTypeRegistry.addContextType(ContextType.Context.key);
+			}
+
+			return contextTypeRegistry;
+		}
+
+		public ContributionTemplateStore getTemplateStore() {
+			if (templateStore == null) {
+				final IPreferenceStore preferenceStore = TextEditorPlugin
+						.getPlugin().getPreferenceStore();
+				templateStore = new ContributionTemplateStore(
+						getContextTypeRegistry(), preferenceStore, "templates");
+				try {
+					templateStore.load();
+				} catch (final IOException e) {
+					TextEditorPlugin.getPlugin().getLog().log(
+							new Status(IStatus.ERROR,
+									TextEditorPlugin.PLUGIN_ID,
+									"Cannot load templates", e));
+				}
+			}
+
+			return templateStore;
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/Builder.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/Builder.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae2b262e98871b0b17514b1f10860576763d4ce6
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/Builder.java
@@ -0,0 +1,54 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.jface.text.IDocument;
+import org.eventb.texteditor.ui.build.ast.AstBuilder;
+import org.eventb.texteditor.ui.build.dom.DomBuilder;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+public class Builder {
+	private final List<IBuildPhase> buildPhases = new ArrayList<IBuildPhase>();
+	private boolean successful;
+
+	public Builder() {
+		buildPhases.add(new AstBuilder());
+		buildPhases.add(new DomBuilder());
+	}
+
+	public void run(final EventBTextEditor editor, final IDocument document,
+			final IProgressMonitor monitor) {
+		final Resource resource = editor.getResource();
+
+		final SubMonitor subMonitor = SubMonitor.convert(monitor, "Building '"
+				+ resource + "'...", buildPhases.size());
+		successful = true;
+
+		for (final IBuildPhase phase : buildPhases) {
+			if (subMonitor.isCanceled()) {
+				return;
+			}
+
+			phase.run(editor, resource, document, subMonitor.newChild(1));
+
+			if (phase.canFail() && !phase.wasSuccessful()) {
+				successful = false;
+				return;
+			}
+		}
+	}
+
+	public boolean wasSuccessful() {
+		return successful;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/IBuildPhase.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/IBuildPhase.java
new file mode 100644
index 0000000000000000000000000000000000000000..3dea07136b41b5fa9e5fb3bd1100af93ca4f2d78
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/IBuildPhase.java
@@ -0,0 +1,50 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.jface.text.IDocument;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+public interface IBuildPhase {
+	/**
+	 * Returns whether this phase involves UI related work.
+	 * 
+	 * @return
+	 */
+	public boolean isUIPhase();
+
+	/**
+	 * Can this build phase fail? If <code>true</code> the method
+	 * {@link #wasSuccessful()} might be called after running
+	 * {@link #run(IProgressMonitor)}.
+	 * 
+	 * @see #wasSuccessful()
+	 * @return
+	 */
+	public boolean canFail();
+
+	/**
+	 * Has the call of {@link #run(IProgressMonitor)}, i.e. the run of this
+	 * build phase been successful? It only makes sense to return
+	 * <code>false</code> if {@link #canFail()} returned <code>true</code>,
+	 * otherwise the result will be ignored anyway.
+	 * 
+	 * @see #canFail()
+	 * @return
+	 */
+	public boolean wasSuccessful();
+
+	/**
+	 * Run this build phase.
+	 * 
+	 * @param monitor
+	 */
+	public void run(final EventBTextEditor editor, final Resource resource,
+			final IDocument document, final IProgressMonitor monitor);
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ReconcilingStrategy.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ReconcilingStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..e375ab36a97fea7587d0795adb826ff17388421e
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ReconcilingStrategy.java
@@ -0,0 +1,59 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.reconciler.DirtyRegion;
+import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
+import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+public class ReconcilingStrategy implements IReconcilingStrategy,
+		IReconcilingStrategyExtension {
+
+	private IDocument document;
+	private IProgressMonitor monitor;
+	private final EventBTextEditor editor;
+
+	public ReconcilingStrategy(final EventBTextEditor editor) {
+		this.editor = editor;
+	}
+
+	public void initialReconcile() {
+		monitor.beginTask("Reconciling '" + editor.getEditorInput().getName()
+				+ "'", 3);
+
+		if (document != null && !editor.isSaving() && !editor.isInLinkedMode()) {
+			new Builder().run(editor, document, monitor);
+		}
+
+		monitor.done();
+	}
+
+	public void reconcile(final IRegion partition) {
+		initialReconcile();
+	}
+
+	public void reconcile(final DirtyRegion dirtyRegion, final IRegion subRegion) {
+		initialReconcile();
+	}
+
+	public void setDocument(final IDocument document) {
+		this.document = document;
+	}
+
+	public void setProgressMonitor(final IProgressMonitor monitor) {
+		if (monitor != null) {
+			this.monitor = monitor;
+		} else {
+			this.monitor = new NullProgressMonitor();
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/AstBuilder.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/AstBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..69d1a8505e6a9f318b2648e41e2bed4f18d19eed
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/AstBuilder.java
@@ -0,0 +1,143 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.util.BasicDiagnostic;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.jface.text.IDocument;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.texteditor.ui.MarkerHelper;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.build.IBuildPhase;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+import org.eventb.texttools.ParseException;
+import org.eventb.texttools.Parser;
+import org.eventb.texttools.PersistenceHelper;
+
+public class AstBuilder implements IBuildPhase {
+
+	private final FormulaTranslator translator = new FormulaTranslator();
+	private final Parser textParser = new Parser();
+
+	private boolean successful;
+
+	public boolean isUIPhase() {
+		return false;
+	}
+
+	public boolean canFail() {
+		return true;
+	}
+
+	public boolean wasSuccessful() {
+		return successful;
+	}
+
+public void run(final EventBTextEditor editor, final Resource resource,
+			final IDocument document, final IProgressMonitor monitor) {
+		successful = false;
+		
+		final EventBNamedCommentedComponentElement astRoot = build(resource, document, true, editor,
+				monitor);
+
+		if (astRoot != null) {
+			final String docContent = document.get();
+			TextEditorPlugin.getDomManager().storeParseResult(
+					editor.getEditorInput(), docContent, astRoot);
+			successful = true;
+		} else {
+			successful = false;
+		}
+	}
+
+	private EventBNamedCommentedComponentElement build(final Resource resource,
+			final IDocument document, final boolean markFormulaErrors,
+			final EventBTextEditor editor, IProgressMonitor monitor) {
+		if (monitor == null) {
+			monitor = new NullProgressMonitor();
+		}
+
+		final EventBNamedCommentedComponentElement astRoot = createAST(resource, document);
+		monitor.worked(1);
+
+		if (monitor.isCanceled()) {
+			return astRoot;
+		}
+
+		if (astRoot != null) {
+			/*
+			 * Translate formula input from ASCII to mathmatical language
+			 */
+			monitor.subTask("Translating formulas");
+			translator.translateFormulas(astRoot, document, editor);
+			monitor.worked(1);
+
+			if (monitor.isCanceled()) {
+				return null;
+			}
+
+			/*
+			 * Resolver can work even if some formulas could not be translated.
+			 * Other formulas might be ok. Anyway we want to highlight errors.
+			 */
+			monitor.subTask("Resolving formulas");
+			FormulasResolver.resolveFormulas(resource, astRoot, document,
+					markFormulaErrors);
+			monitor.worked(1);
+
+			/*
+			 * Add text representation as annotation to resource's model
+			 */
+			PersistenceHelper.addTextAnnotation(astRoot, document.get(), System
+					.currentTimeMillis());
+		}
+
+		return astRoot;
+	}
+
+	private EventBNamedCommentedComponentElement createAST(final Resource resource,
+			final IDocument document) {
+		/*
+		 * Clean up all markers which belong to our tool (including subtypes)
+		 * 'cause we don't know whether following tools will be able to run,
+		 * i.e., whether parsing will be successful.
+		 */
+		final MarkerHelper helper = new MarkerHelper(TextEditorPlugin.SYNTAXERROR_MARKER_ID);
+		helper.deleteMarkers(resource, true, IResource.DEPTH_INFINITE);
+
+		EventBNamedCommentedComponentElement component = null;
+
+		try {
+			component = textParser.parse(document);
+		} catch (final ParseException e) {
+			final IParseProblemWrapper wrappedException = new ParseExceptionWrapperDiagnostic(
+					document, e);
+
+			final BasicDiagnostic diagnostic = new BasicDiagnostic(
+					Diagnostic.ERROR, TextEditorPlugin.PLUGIN_ID, 0, e
+							.getLocalizedMessage(), new Object[] { resource,
+							wrappedException });
+
+			try {
+				helper.createMarkers(diagnostic);
+			} catch (final CoreException ex) {
+				TextEditorPlugin.getPlugin().getLog().log(
+						new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID,
+								"Could not create markers", ex));
+			}
+		}
+
+		return component;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/ContextTranslateSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/ContextTranslateSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..d45ddb165656dce802477059704463a27bddbc45
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/ContextTranslateSwitch.java
@@ -0,0 +1,25 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.context.util.ContextSwitch;
+
+public class ContextTranslateSwitch extends ContextSwitch<Boolean> {
+
+	private final FormulaTranslator translator;
+
+	public ContextTranslateSwitch(final FormulaTranslator formulaTranslator) {
+		translator = formulaTranslator;
+	}
+
+	@Override
+	public Boolean caseEventBNamedCommentedPredicateElement(final EventBNamedCommentedPredicateElement object) {
+		translator.replace(object);
+		return true;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulaExceptionWrapperDiagnostic.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulaExceptionWrapperDiagnostic.java
new file mode 100644
index 0000000000000000000000000000000000000000..905df6e5e4a2988eaf89fdea0daaae958e151162
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulaExceptionWrapperDiagnostic.java
@@ -0,0 +1,104 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eventb.core.ast.ASTProblem;
+import org.eventb.core.ast.SourceLocation;
+
+public class FormulaExceptionWrapperDiagnostic implements IParseProblemWrapper {
+
+	final ASTProblem problem;
+	private final IDocument document;
+	private final int startOffset;
+
+	public FormulaExceptionWrapperDiagnostic(final ASTProblem problem,
+			final int formulaOffset, final IDocument document) {
+		this.problem = problem;
+		startOffset = formulaOffset;
+		this.document = document;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getColumn()
+	 */
+	public int getColumn() {
+		try {
+			final int offset = getOffset();
+			return offset
+					- document.getLineOffset(document.getLineOfOffset(offset));
+		} catch (final BadLocationException e) {
+			// IGNORE
+			return 0;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getLine()
+	 */
+	public int getLine() {
+		try {
+			return document.getLineOfOffset(getOffset());
+		} catch (final BadLocationException e) {
+			// IGNORE
+			return 0;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getOffset()
+	 */
+	public int getOffset() {
+		return startOffset + problem.getSourceLocation().getStart();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getTokenLength
+	 * ()
+	 */
+	public int getTokenLength() {
+		final SourceLocation location = problem.getSourceLocation();
+		return location.getEnd() - location.getStart();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getToken()
+	 */
+	public String getToken() {
+		try {
+			return document.get(getOffset(), getTokenLength());
+		} catch (final BadLocationException e) {
+			// IGNORE and return fallback value
+			return "";
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getMessage()
+	 */
+	public String getMessage() {
+		return String
+				.format(problem.getMessage().toString(), problem.getArgs());
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulaTranslator.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulaTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad61ba630c53520dd6e9aa26e089aa17b0ce275e
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulaTranslator.java
@@ -0,0 +1,189 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.swt.widgets.Display;
+import org.eventb.emf.core.EventBCommentedExpressionElement;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.eventBKeyboard.Text2EventBMathTranslator;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.model.texttools.TextRange;
+
+public class FormulaTranslator {
+	private IDocument document;
+	private EventBTextEditor editor;
+
+	public void translateFormulas(
+			final EventBNamedCommentedComponentElement astRoot,
+			final IDocument document, final EventBTextEditor editor) {
+		this.document = document;
+		this.editor = editor;
+
+		// traverse tree using an iterator
+		final TreeIterator<EObject> iterator = EcoreUtil.getAllContents(
+				astRoot, false);
+
+		if (astRoot instanceof Machine) {
+			final MachineTranslateSwitch switcher = new MachineTranslateSwitch(
+					this);
+			while (iterator.hasNext()) {
+				// visit node
+				switcher.doSwitch(iterator.next());
+			}
+		} else if (astRoot instanceof Context) {
+			final ContextTranslateSwitch switcher = new ContextTranslateSwitch(
+					this);
+			while (iterator.hasNext()) {
+				// visit node
+				switcher.doSwitch(iterator.next());
+			}
+		}
+	}
+
+	public void replace(final EventBNamedCommentedPredicateElement predicate) {
+		final String input = predicate.getPredicate();
+
+		if (input != null) {
+			final String translatedInput = Text2EventBMathTranslator
+					.translate(input);
+
+			if (!input.equals(translatedInput)) {
+				// replace in emf node
+				predicate.setPredicate(translatedInput);
+				final TextRange range = updatePosition(predicate, input,
+						translatedInput);
+
+				updateDocument(input, translatedInput, range);
+			}
+		}
+	}
+
+	public void replace(final EventBCommentedExpressionElement expression) {
+		final String input = expression.getExpression();
+
+		if (input != null) {
+			final String translatedInput = Text2EventBMathTranslator
+					.translate(input);
+
+			if (!input.equals(translatedInput)) {
+				// replace in emf node
+				expression.setExpression(translatedInput);
+				final TextRange range = updatePosition(expression, input,
+						translatedInput);
+
+				updateDocument(input, translatedInput, range);
+			}
+		}
+	}
+
+	public void replace(final Action action) {
+		final String input = action.getAction();
+
+		if (input != null) {
+			final String translatedInput = Text2EventBMathTranslator
+					.translate(input);
+
+			if (!input.equals(translatedInput)) {
+				// replace in emf node
+				action.setAction(translatedInput);
+				final TextRange range = updatePosition(action, input,
+						translatedInput);
+
+				updateDocument(input, translatedInput, range);
+			}
+		}
+	}
+
+	private void updateDocument(final String input,
+			final String translatedInput, final TextRange range) {
+		/*
+		 * Check length and fill output string with whitespaces to length of
+		 * input. So we avoid position changes for the following text elements.
+		 * Output should always be <= input.
+		 */
+		final int lengthDiff = input.length() - translatedInput.length();
+		Assert
+				.isTrue(lengthDiff >= 0,
+						"Expecting length of translated formula to be less or equal to original length");
+		final String filledString = fillOutput(translatedInput, lengthDiff);
+
+		Display.getDefault().asyncExec(new Runnable() {
+			public void run() {
+				try {
+					int cursorOffset = -1;
+					if (editor != null) {
+						cursorOffset = editor.getCursorOffset();
+					}
+
+					/*
+					 * Replace text in document. Important: Don't use length
+					 * from TextRange because it's only set to the short new
+					 * version without the trailing whitespaces.
+					 */
+					final int startOffset = range.getOffset();
+					document.replace(startOffset, filledString.length(),
+							filledString);
+
+					// correct cursor position afterwards
+					if (editor != null && cursorOffset >= 0) {
+						correctCursorPosition(cursorOffset, range, lengthDiff);
+					}
+				} catch (final BadLocationException e) {
+					// IGNORE, cannot fix it anyway
+				}
+			}
+		});
+	}
+
+	private void correctCursorPosition(final int oldOffset,
+			final TextRange range, final int lengthCorrection) {
+		// was cursor in changed region?
+		if (oldOffset >= range.getOffset()
+				&& oldOffset <= range.getOffset() + range.getLength()
+						+ lengthCorrection) {
+			// then move cursor to front a bit
+			editor.setCurserOffset(oldOffset - lengthCorrection);
+		}
+	}
+
+	private String fillOutput(final String output, final int count) {
+		if (count > 0) {
+			final StringBuilder buffer = new StringBuilder(output);
+			for (int i = 0; i < count; i++) {
+				buffer.append(' ');
+			}
+
+			return buffer.toString();
+		} else {
+			return output;
+		}
+	}
+
+	private TextRange updatePosition(final EventBElement parent,
+			final String oldContent, final String newContent) {
+		final TextRange range = TextPositionUtil.getInternalPosition(parent,
+				oldContent);
+
+		range.setLength(newContent.length());
+		TextPositionUtil.replaceInternalPosition(parent, oldContent,
+				newContent, range);
+
+		return range;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulasResolver.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulasResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c72589cdf728788d1da76c4759f9a0f00e92dbc
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/FormulasResolver.java
@@ -0,0 +1,94 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.emf.common.util.BasicDiagnostic;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.jface.text.IDocument;
+import org.eventb.core.ast.ASTProblem;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.texteditor.ui.MarkerHelper;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.formulas.FormulaParseException;
+import org.eventb.texttools.formulas.FormulaResolver;
+import org.eventb.texttools.model.texttools.TextRange;
+import org.rodinp.core.RodinMarkerUtil;
+
+public class FormulasResolver {
+
+	private static final MarkerHelper markerHelper = new MarkerHelper(
+			TextEditorPlugin.SYNTAXERROR_MARKER_ID);
+
+	public static void resolveFormulas(final Resource resource,
+			final EventBNamedCommentedComponentElement astRoot, final IDocument document,
+			final boolean markErrors) {
+
+		final List<FormulaParseException> exceptions = FormulaResolver
+				.resolveAllFormulas(astRoot);
+
+		if (markErrors && exceptions.size() > 0) {
+			markerHelper.deleteMarkers(resource,
+					RodinMarkerUtil.RODIN_PROBLEM_MARKER, true,
+					IResource.DEPTH_ZERO);
+			markErrors(exceptions, resource, document);
+		}
+	}
+
+	private static void markErrors(
+			final List<FormulaParseException> exceptions,
+			final Resource resource, final IDocument document) {
+		// TODO Do we really want to add a marker/diagnostic to the resource?
+		// When the model is saved the RodinDB will create another marker for
+		// it. Maybe having a marker in the texteditor is enough.
+
+		final BasicDiagnostic diagnostic = new BasicDiagnostic(
+				Diagnostic.ERROR, TextEditorPlugin.PLUGIN_ID, 0,
+				"Error occured when parsing the formulas",
+				new Object[] { resource });
+
+		for (final FormulaParseException ex : exceptions) {
+			final String inputFormula = ex.getFormula();
+			final TextRange range = TextPositionUtil.getInternalPosition(ex
+					.getEmfObject(), inputFormula);
+			final int offset = range.getOffset();
+
+			final List<ASTProblem> problems = ex.getAstProblems();
+			for (final ASTProblem problem : problems) {
+				diagnostic.add(createChildDiagnostic(problem, offset,
+						inputFormula, document, resource));
+			}
+		}
+
+		try {
+			markerHelper.createMarkers(diagnostic);
+		} catch (final CoreException e) {
+			TextEditorPlugin.INSTANCE.log(e);
+		}
+
+	}
+
+	private static Diagnostic createChildDiagnostic(final ASTProblem problem,
+			final int offset, final String inputFormula,
+			final IDocument document, final Resource resource) {
+
+		final IParseProblemWrapper wrappedException = new FormulaExceptionWrapperDiagnostic(
+				problem, offset, document);
+
+		final BasicDiagnostic diagnostic = new BasicDiagnostic(
+				Diagnostic.ERROR, TextEditorPlugin.PLUGIN_ID, 0,
+				wrappedException.getMessage(), new Object[] { resource,
+						wrappedException });
+
+		return diagnostic;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/IParseProblemWrapper.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/IParseProblemWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..295564c86aea08d9a906b7809cb767fd05e964bc
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/IParseProblemWrapper.java
@@ -0,0 +1,23 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+public interface IParseProblemWrapper {
+
+	public abstract int getColumn();
+
+	public abstract int getLine();
+
+	public abstract int getOffset();
+
+	public abstract int getTokenLength();
+
+	public abstract String getToken();
+
+	public abstract String getMessage();
+
+}
\ No newline at end of file
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/MachineTranslateSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/MachineTranslateSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8d860b5efa1c78e9b513de279bb06c9e26aa7ce
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/MachineTranslateSwitch.java
@@ -0,0 +1,39 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+import org.eventb.emf.core.EventBCommentedExpressionElement;
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.util.MachineSwitch;
+
+public class MachineTranslateSwitch extends MachineSwitch<Boolean> {
+
+	private final FormulaTranslator translator;
+
+	public MachineTranslateSwitch(final FormulaTranslator formulaTranslator) {
+		translator = formulaTranslator;
+	}
+
+	@Override
+	public Boolean caseEventBNamedCommentedPredicateElement(final EventBNamedCommentedPredicateElement object) {
+		translator.replace(object);
+		return true;
+	}
+
+	@Override
+	public Boolean caseEventBCommentedExpressionElement(final EventBCommentedExpressionElement object) {
+		translator.replace(object);
+		return true;
+	}
+
+	@Override
+	public Boolean caseAction(final Action object) {
+		translator.replace(object);
+		return true;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/ParseExceptionWrapperDiagnostic.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/ParseExceptionWrapperDiagnostic.java
new file mode 100644
index 0000000000000000000000000000000000000000..7fd7305aeb8d2c37a335e98b7d4fca024b0cf135
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/ast/ParseExceptionWrapperDiagnostic.java
@@ -0,0 +1,75 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.ast;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eventb.texttools.ParseException;
+
+public class ParseExceptionWrapperDiagnostic implements IParseProblemWrapper {
+
+	private final ParseException exception;
+	private final IDocument document;
+
+	public ParseExceptionWrapperDiagnostic(final IDocument document,
+			final ParseException e) {
+		this.document = document;
+		exception = e;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getColumn()
+	 */
+	public int getColumn() {
+		return exception.getPosition();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getLine()
+	 */
+	public int getLine() {
+		return exception.getLine();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getOffset()
+	 */
+	public int getOffset() {
+		try {
+			return document.getLineOffset(getLine()) + getColumn();
+		} catch (final BadLocationException e) {
+			// IGNORE and return fallback value
+			return 0;
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getTokenLength()
+	 */
+	public int getTokenLength() {
+		return exception.getTokenLength();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getToken()
+	 */
+	public String getToken() {
+		try {
+			return document.get(getOffset(), getTokenLength());
+		} catch (final BadLocationException e) {
+			// IGNORE and return fallback value
+			return "";
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eventb.emf.texteditor2.build.ast.IParseProblemWrapper#getMessage()
+	 */
+	public String getMessage() {
+		return exception.getLocalizedMessage();
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/AbstractComponentDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/AbstractComponentDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff7cf685633f297210ef37590071093c6b0ccce7
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/AbstractComponentDom.java
@@ -0,0 +1,69 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+
+public abstract class AbstractComponentDom extends AbstractDom implements
+		IComponentDom {
+
+	protected boolean initialized = false;
+	private boolean initializing = false;
+	private final Resource resource;
+
+	protected AbstractComponentDom(final Type type, final IDom parent,
+			final Resource resource) {
+		super(type, parent);
+		this.resource = resource;
+	}
+
+	@Override
+	public synchronized void reset() {
+		if (initialized) {
+			super.reset();
+			doReset();
+			initialized = false;
+		}
+	}
+
+	public synchronized void resetAndinit() {
+		reset();
+		init();
+	}
+
+	public Resource getResource() {
+		return resource;
+	}
+
+	protected void checkInitialization() {
+		if (!isInitialized()) {
+			init();
+		}
+	}
+
+	private synchronized void init() {
+		if (!initialized && !initializing) {
+			initializing = true;
+			doInitialize();
+			initialized = true;
+			initializing = false;
+		}
+	}
+
+	private synchronized boolean isInitialized() {
+		return initialized;
+	}
+
+	protected void doInitialize() {
+		DomBuilder.initializeDOM(this,
+				(EventBNamedCommentedComponentElement) getEventBElement(),
+				resource);
+	}
+
+	protected abstract void doReset();
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/AbstractDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/AbstractDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..18a6a1180b5e625ceac4b8bf4aed813b6ebef59f
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/AbstractDom.java
@@ -0,0 +1,105 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.model.texttools.TextRange;
+
+public abstract class AbstractDom implements IDom {
+
+	private final Type type;
+	private final IDom parent;
+	private final List<IDom> children = new LinkedList<IDom>();
+
+	protected AbstractDom(final Type type, final IDom parent) {
+		this.type = type;
+		this.parent = parent;
+	}
+
+	public Type getType() {
+		return type;
+	}
+
+	public IDom getParent() {
+		return parent;
+	}
+
+	public synchronized void reset() {
+		children.clear();
+	}
+
+	/**
+	 * Returns a {@link List} with all child DOMs of this DOM. <b>Attention:</b>
+	 * The returned list is unmodifiable and will throw
+	 * {@link UnsupportedOperationException}s for modification method calls.
+	 */
+	public synchronized List<IDom> getChildren() {
+		return Collections.unmodifiableList(children);
+	}
+
+	public synchronized void addChild(final IDom child) {
+		children.add(child);
+	}
+
+	public synchronized IDom getScopingDom(final int offset) {
+		if (!containsOffset(TextPositionUtil.getTextRange(getEventBElement()),
+				offset)) {
+			return null;
+		}
+
+		for (final IDom child : children) {
+			final IDom scope = child.getScopingDom(offset);
+			if (scope != null) {
+				return scope;
+			}
+		}
+
+		return this;
+	}
+
+	public Set<String> getIdentifiers() {
+		final Set<String> result = new HashSet<String>();
+
+		if (parent != null) {
+			result.addAll(parent.getIdentifiers());
+		}
+
+		result.addAll(doGetIdentifiers());
+
+		return result;
+	}
+
+	public IdentifierType getIdentifierType(final String identifier) {
+		IDom currentDom = this;
+		IdentifierType type = null;
+
+		// search identifier upwarts in hierarchy
+		while (currentDom != null
+				&& (type = ((AbstractDom) currentDom)
+						.doGetIdentifierType(identifier)) == null) {
+			currentDom = currentDom.getParent();
+		}
+
+		return type;
+	}
+
+	private boolean containsOffset(final TextRange range, final int offset) {
+		return range != null && range.getOffset() <= offset
+				&& range.getOffset() + range.getLength() >= offset;
+	}
+
+	protected abstract Set<String> doGetIdentifiers();
+
+	protected abstract IdentifierType doGetIdentifierType(
+			final String identifier);
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/CollectingHelper.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/CollectingHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..2faa9f6675bce6f695debe1a72ef12bba54f0974
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/CollectingHelper.java
@@ -0,0 +1,65 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.URIConverter;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.emf.core.machine.Machine;
+
+public class CollectingHelper {
+	public static final String SUFFIX_MACHINE = "bum";
+	public static final String SUFFIX_CONTEXT = "buc";
+
+	protected static Resource resolveComponentsResource(
+			final EventBNamedCommentedComponentElement component, final Resource referencingResource) {
+		Resource resource = null;
+
+		if (component.eIsProxy()) {
+			// resolve context in resource's ResourceSet
+			final EventBNamedCommentedComponentElement resolvedComponent = (EventBNamedCommentedComponentElement) EcoreUtil
+					.resolve(component, referencingResource);
+
+			/*
+			 * Workaround when automatic resolving didn't work.
+			 */
+			if (resolvedComponent.eIsProxy()) {
+				final URI uri = referencingResource.getURI()
+						.trimFileExtension().trimSegments(1).appendSegment(
+								((EventBNamed) component).getName())
+						.appendFileExtension(getFileExtension(component));
+				/*
+				 * Only try to resolve if file already exists. We don't want new
+				 * files to be created on the fly.
+				 */
+				if (URIConverter.INSTANCE.exists(uri, null)) {
+					resource = referencingResource.getResourceSet()
+							.getResource(uri, true);
+				} else {
+					resource = resolvedComponent.eResource();
+				}
+			} else {
+				resource = resolvedComponent.eResource();
+			}
+		} else {
+			resource = component.eResource();
+		}
+
+		return resource;
+	}
+
+	private static String getFileExtension(final EventBNamedCommentedComponentElement component) {
+		if (component instanceof Machine) {
+			return SUFFIX_MACHINE;
+		} else {
+			return SUFFIX_CONTEXT;
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/ContextCollectingSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/ContextCollectingSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..020e9e74edd3f7ae8c097f4c5a246d4f638e2d36
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/ContextCollectingSwitch.java
@@ -0,0 +1,92 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.Axiom;
+import org.eventb.emf.core.context.CarrierSet;
+import org.eventb.emf.core.context.Constant;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.context.util.ContextSwitch;
+import org.eventb.emf.formulas.BFormula;
+import org.eventb.texttools.formulas.ExtensionHelper;
+
+public class ContextCollectingSwitch extends ContextSwitch<Boolean> {
+	private final ContextDom contextDom;
+	private final Resource contextResource;
+
+	protected ContextCollectingSwitch(final ContextDom dom,
+			final Resource contextResource) {
+		contextDom = dom;
+		this.contextResource = contextResource;
+	}
+
+	protected AbstractDom getDom() {
+		return contextDom;
+	}
+
+	public IDom getCurrentParentDom() {
+		return contextDom;
+	}
+
+	@Override
+	public Boolean caseEventBObject(final EventBObject object) {
+		// default to avoid NPEs
+		return true;
+	}
+
+	@Override
+	public Boolean caseContext(final Context object) {
+		contextDom.setContext(object);
+
+		final EList<Context> extendedContexts = object.getExtends();
+		for (final Context context : extendedContexts) {
+			final IComponentDom dom = DomBuilder.getReferencedDom(context,
+					contextResource);
+
+			if (dom != null) {
+				Assert.isTrue(dom instanceof ContextDom);
+				contextDom.addExtendedContext((ContextDom) dom);
+			}
+
+			/*
+			 * If no DOM is found the reference must be to a not-existing
+			 * machine.
+			 */
+		}
+
+		return true;
+	}
+
+	@Override
+	public Boolean caseAxiom(final Axiom object) {
+		final BFormula formula = ExtensionHelper.getFormula(object
+				.getExtensions());
+
+		if (formula != null) {
+			final FormulaDom formDom = new FormulaDom(formula, contextDom);
+			contextDom.addChild(formDom);
+		}
+
+		return false;
+	}
+
+	@Override
+	public Boolean caseConstant(final Constant object) {
+		contextDom.addConstant(object);
+		return false;
+	}
+
+	@Override
+	public Boolean caseCarrierSet(final CarrierSet object) {
+		contextDom.addSet(object);
+		return false;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/ContextDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/ContextDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..c539721d568a72f82e218db2f73bc5362dffb587
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/ContextDom.java
@@ -0,0 +1,140 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.CarrierSet;
+import org.eventb.emf.core.context.Constant;
+import org.eventb.emf.core.context.Context;
+
+public class ContextDom extends AbstractComponentDom {
+	private Context context;
+	private final Map<String, Constant> constants = new HashMap<String, Constant>();
+	private final Map<String, CarrierSet> sets = new HashMap<String, CarrierSet>();
+
+	private final List<ContextDom> extendedContexts = new LinkedList<ContextDom>();
+
+	ContextDom(final Resource resource, final Context context) {
+		super(Type.Context, null, resource);
+		this.context = context;
+	}
+
+	@Override
+	protected synchronized void doReset() {
+		constants.clear();
+		sets.clear();
+		extendedContexts.clear();
+	}
+
+	@Override
+	protected synchronized Set<String> doGetIdentifiers() {
+		final Set<String> result = new HashSet<String>();
+
+		result.addAll(getConstants(true).keySet());
+		result.addAll(getSets(true).keySet());
+
+		return result;
+	}
+
+	@Override
+	protected synchronized IdentifierType doGetIdentifierType(
+			final String identifier) {
+		if (getConstants(true).containsKey(identifier)) {
+			return IdentifierType.Constant;
+		}
+
+		if (getSets(true).containsKey(identifier)) {
+			return IdentifierType.Set;
+		}
+
+		return null;
+	}
+
+	public Set<IComponentDom> getReferencedDoms(final boolean transitive) {
+		final List<ContextDom> contexts = getExtendedContexts();
+		final HashSet<IComponentDom> result = new HashSet<IComponentDom>(
+				contexts);
+
+		if (transitive) {
+			for (final IComponentDom referencedDom : contexts) {
+				result.addAll(referencedDom.getReferencedDoms(true));
+			}
+		}
+
+		return result;
+	}
+
+	public synchronized List<ContextDom> getExtendedContexts() {
+		checkInitialization();
+		return extendedContexts;
+	}
+
+	public synchronized Map<String, Constant> getConstants(
+			final boolean includeInherited) {
+		checkInitialization();
+
+		final Map<String, Constant> result = new HashMap<String, Constant>();
+
+		if (includeInherited) {
+			// add referenced context's constants
+			for (final ContextDom dom : getExtendedContexts()) {
+				result.putAll(dom.getConstants(includeInherited));
+			}
+		}
+		// finally the local constants
+		result.putAll(constants);
+
+		return result;
+	}
+
+	public synchronized Map<String, CarrierSet> getSets(
+			final boolean includeInherited) {
+		checkInitialization();
+
+		final Map<String, CarrierSet> result = new HashMap<String, CarrierSet>();
+
+		if (includeInherited) {
+			// add referenced context's constants
+			for (final ContextDom dom : getExtendedContexts()) {
+				result.putAll(dom.getSets(includeInherited));
+			}
+		}
+
+		// finally the local sets
+		result.putAll(sets);
+
+		return result;
+	}
+
+	synchronized void addExtendedContext(final ContextDom dom) {
+		extendedContexts.add(dom);
+	}
+
+	synchronized void addConstant(final Constant constant) {
+		constants.put(constant.getName(), constant);
+	}
+
+	synchronized void addSet(final CarrierSet set) {
+		sets.put(set.getName(), set);
+	}
+
+	public synchronized EventBObject getEventBElement() {
+		return context;
+	}
+
+	public void setContext(final Context context) {
+		this.context = context;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/DomBuilder.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/DomBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c54eaf0dab35e0b9e9d0dd46187eae71ce3db07
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/DomBuilder.java
@@ -0,0 +1,204 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ui.IEditorInput;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.context.ContextPackage;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.MachinePackage;
+import org.eventb.emf.formulas.FormulasPackage;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.build.IBuildPhase;
+import org.eventb.texteditor.ui.build.dom.DomManager.ParseResult;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+public class DomBuilder implements IBuildPhase {
+
+	public boolean isUIPhase() {
+		return false;
+	}
+
+	public boolean canFail() {
+		return false;
+	}
+
+	public boolean wasSuccessful() {
+		return true;
+	}
+
+	public void run(final EventBTextEditor editor, final Resource resource,
+			final IDocument document, final IProgressMonitor monitor) {
+		final DomManager domManager = TextEditorPlugin.getDomManager();
+		final IEditorInput editorInput = editor.getEditorInput();
+		final ParseResult parseResult = domManager
+				.getLastParseResult(editorInput);
+
+		if (parseResult != null) {
+			final EventBNamedCommentedComponentElement astRoot = (EventBNamedCommentedComponentElement) parseResult.astRoot;
+			final IComponentDom dom = domManager.getDom(resource, astRoot);
+
+			if (astRoot instanceof Machine) {
+				((MachineDom) dom).setMachine((Machine) astRoot);
+			} else if (astRoot instanceof Context) {
+				((ContextDom) dom).setContext((Context) astRoot);
+			}
+
+			dom.resetAndinit();
+		}
+	}
+
+	/**
+	 * Find the {@link IComponentDom} for the given {@link EventBNamedCommentedComponentElement}. The
+	 * given {@link Resource} is used as a base when resolving the resource for
+	 * the referenced component.
+	 *
+	 * @param component
+	 * @param referencingResource
+	 * @return
+	 */
+	public static IComponentDom getReferencedDom(
+			final EventBNamedCommentedComponentElement component, final Resource referencingResource) {
+		final Resource resource = CollectingHelper.resolveComponentsResource(
+				component, referencingResource);
+
+		if (resource != null) {
+			return TextEditorPlugin.getDomManager().getDom(resource, component);
+		}
+
+		return null;
+	}
+
+	/**
+	 * Initializes the given {@link IComponentDom} with the given
+	 * {@link EventBNamedCommentedComponentElement}. For all referenced {@link EventBNamedCommentedComponentElement} the
+	 * given {@link Resource} is used as the base from where to search the
+	 * referenced {@link Resource}.
+	 *
+	 * @param dom
+	 * @param component
+	 * @param resource
+	 * @return
+	 */
+	public static void initializeDOM(final IComponentDom dom,
+			final EventBNamedCommentedComponentElement component, final Resource resource) {
+		if (component instanceof Machine) {
+			initializeDOM((MachineDom) dom, (Machine) component, resource);
+		} else if (component instanceof Context) {
+			initializeDOM((ContextDom) dom, (Context) component, resource);
+		}
+
+		// notify listeners in dom manager about change
+		TextEditorPlugin.getDomManager().notifyDomChangeListeners(dom);
+	}
+
+	/**
+	 * Initializes the given {@link MachineDom} with the given {@link Machine}.
+	 * For all referenced {@link EventBNamedCommentedComponentElement} the given {@link Resource} is
+	 * used as the base from where to search the referenced {@link Resource}.
+	 *
+	 * @param dom
+	 * @param component
+	 * @param resource
+	 * @return
+	 */
+	private static void initializeDOM(final MachineDom dom,
+			final Machine component, final Resource resource) {
+		dom.reset();
+
+		final MachineCollectingSwitch machineSwitch = new MachineCollectingSwitch(
+				dom, resource);
+		machineSwitch.doSwitch(component);
+		final FormulaCollectingSwitch formulaSwitch = new FormulaCollectingSwitch();
+
+		// traverse tree using an iterator
+		final TreeIterator<EObject> iterator = getContentIterator(component,
+				resource);
+
+		while (iterator.hasNext()) {
+			final EObject next = iterator.next();
+			final String nsURI = next.eClass().getEPackage().getNsURI();
+			Boolean visitChildren = true;
+
+			if (MachinePackage.eNS_URI.equals(nsURI)) {
+				visitChildren = machineSwitch.doSwitch(next);
+			} else if (FormulasPackage.eNS_URI.equals(nsURI)) {
+				formulaSwitch.setCurrentParentDom(machineSwitch
+						.getCurrentParentDom());
+				formulaSwitch.doSwitch(next);
+			}
+
+			// visit node
+			if (!visitChildren) {
+				// skip children if visited node returne false
+				iterator.prune();
+			}
+		}
+	}
+
+	/**
+	 * Initializes the given {@link ContextDom} with the given {@link Context}.
+	 * For all referenced {@link EventBNamedCommentedComponentElement} the given {@link Resource} is
+	 * used as the base from where to search the referenced {@link Resource}.
+	 *
+	 * @param dom
+	 * @param component
+	 * @param resource
+	 * @return
+	 */
+	private static void initializeDOM(final ContextDom dom,
+			final Context component, final Resource resource) {
+		dom.reset();
+
+		final ContextCollectingSwitch contextSwitch = new ContextCollectingSwitch(
+				dom, resource);
+		contextSwitch.doSwitch(component);
+		final FormulaCollectingSwitch formulaSwitch = new FormulaCollectingSwitch();
+
+		// traverse tree using an iterator
+		final TreeIterator<EObject> iterator = getContentIterator(component,
+				resource);
+
+		while (iterator.hasNext()) {
+			final EObject next = iterator.next();
+			final String nsURI = next.eClass().getEPackage().getNsURI();
+			Boolean visitChildren = true;
+
+			if (ContextPackage.eNS_URI.equals(nsURI)) {
+				visitChildren = contextSwitch.doSwitch(next);
+			} else if (FormulasPackage.eNS_URI.equals(nsURI)) {
+				formulaSwitch.setCurrentParentDom(contextSwitch
+						.getCurrentParentDom());
+				formulaSwitch.doSwitch(next);
+			}
+
+			// visit node
+			if (!visitChildren) {
+				// skip children if visited node returne false
+				iterator.prune();
+			}
+		}
+	}
+
+	private static TreeIterator<EObject> getContentIterator(
+			final EventBNamedCommentedComponentElement component, final Resource resource) {
+		if (component.eIsProxy()) {
+			// the component has not been resolved yet, use resource to do so
+			return EcoreUtil.getAllContents(resource, true);
+		} else {
+			// we can use the contents of the component itself
+			return EcoreUtil.getAllContents(component, false);
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/DomManager.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/DomManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..7050e032fe20594df1035ae97384fb22406b6225
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/DomManager.java
@@ -0,0 +1,210 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.ui.IEditorInput;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.machine.Machine;
+
+public class DomManager {
+
+	private final Map<Resource, IComponentDom> lastDom = new HashMap<Resource, IComponentDom>();
+	private final Map<IEditorInput, ParseResult> lastParseResults = new HashMap<IEditorInput, ParseResult>();
+
+	private final Set<IDomChangeListener> domListeners = new HashSet<IDomChangeListener>();
+	private final Map<IEditorInput, List<IParseResultListener>> parseResultListeners = new HashMap<IEditorInput, List<IParseResultListener>>();
+
+	public void storeDom(final IComponentDom dom) {
+		if (dom != null) {
+			synchronized (lastDom) {
+				lastDom.put(dom.getResource(), dom);
+			}
+		}
+	}
+
+	public void removeDom(final Resource resource) {
+		synchronized (lastDom) {
+			lastDom.remove(resource);
+		}
+	}
+
+	/**
+	 * Returns an {@link IComponentDom} for the given {@link Resource} or
+	 * <code>null</code> if none is available. See
+	 * {@link #getDom(Resource, EventBNamedCommentedComponentElement)} if you want lookup and
+	 * on-demand creation.
+	 *
+	 * @see #getDom(Resource, EventBNamedCommentedComponentElement)
+	 * @param resource
+	 * @return
+	 */
+	public IComponentDom getDom(final Resource resource) {
+		synchronized (lastDom) {
+			return lastDom.get(resource);
+		}
+	}
+
+	/**
+	 * Returns an {@link IComponentDom} object for the given {@link Resource}.
+	 * If no DOM is available so far a new and uninitialized one is created for
+	 * the resource and the given {@link EventBNamedCommentedComponentElement}.
+	 *
+	 * @param resource
+	 * @param object
+	 * @return
+	 */
+	public IComponentDom getDom(final Resource resource,
+			final EventBNamedCommentedComponentElement component) {
+		synchronized (lastDom) {
+			IComponentDom dom = lastDom.get(resource);
+
+			if (dom == null && component != null) {
+				dom = createNewDom(resource, component);
+				storeDom(dom);
+			}
+
+			return dom;
+		}
+	}
+
+	/**
+	 * Returns the {@link IComponentDom} object for the given
+	 * {@link EventBObject}'s resource. If no DOM is available so far a new and
+	 * uninitialized one is created.
+	 *
+	 * @see #getDom(Resource)
+	 * @param object
+	 * @return
+	 */
+	public IComponentDom getDom(final EventBNamedCommentedComponentElement component) {
+		final Resource resource = component.eResource();
+
+		if (resource != null) {
+			synchronized (lastDom) {
+				return getDom(resource, component);
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Creates a new and uninitialized {@link IComponentDom} for the given
+	 * {@link EventBNamedCommentedComponentElement}.
+	 *
+	 * @param component
+	 * @return
+	 */
+	private IComponentDom createNewDom(final Resource resource,
+			final EventBNamedCommentedComponentElement component) {
+		IComponentDom dom;
+		if (component instanceof Machine) {
+			dom = new MachineDom(resource, (Machine) component);
+		} else {
+			dom = new ContextDom(resource, (Context) component);
+		}
+
+		return dom;
+	}
+
+	public void storeParseResult(final IEditorInput editorInput,
+			final String textInput, final EventBNamedCommentedComponentElement astRoot) {
+		final ParseResult parseResult = new ParseResult(astRoot, textInput);
+		lastParseResults.put(editorInput, parseResult);
+
+		notifyParseResultListeners(editorInput, parseResult);
+	}
+
+	/**
+	 * <p>
+	 * Returns the last successful parse result, i.e., the last successfully
+	 * parsed text and its AST that was produced.
+	 * </p>
+	 * <p>
+	 * Attention: The result may be old and not consistent with the current
+	 * content of the editor.
+	 * </p>
+	 *
+	 * @param editorInput
+	 * @return
+	 */
+	public ParseResult getLastParseResult(final IEditorInput editorInput) {
+		return lastParseResults.get(editorInput);
+	}
+
+	public void addParseResultListener(final IEditorInput editorInput,
+			final IParseResultListener listener) {
+		if (!parseResultListeners.containsKey(editorInput)) {
+			final ArrayList<IParseResultListener> list = new ArrayList<IParseResultListener>();
+			list.add(listener);
+			parseResultListeners.put(editorInput, list);
+		} else {
+			parseResultListeners.get(editorInput).add(listener);
+		}
+	}
+
+	public void removeParseResultListener(final IEditorInput editorInput,
+			final IParseResultListener listener) {
+		if (parseResultListeners.containsKey(editorInput)) {
+			final List<IParseResultListener> list = parseResultListeners
+					.get(editorInput);
+			list.remove(listener);
+
+			if (list.isEmpty()) {
+				parseResultListeners.remove(editorInput);
+			}
+		}
+	}
+
+	private void notifyParseResultListeners(final IEditorInput editorInput,
+			final ParseResult parseResult) {
+		if (parseResultListeners.containsKey(editorInput)) {
+			for (final IParseResultListener listener : parseResultListeners
+					.get(editorInput)) {
+				listener.parseResultChanged(parseResult);
+			}
+		}
+	}
+
+	public void addDomChangeListener(final IDomChangeListener listener) {
+		if (listener != null && !domListeners.contains(listener)) {
+			domListeners.add(listener);
+		}
+	}
+
+	public void removeDomChangeListener(final IDomChangeListener listener) {
+		if (listener != null) {
+			domListeners.remove(listener);
+		}
+	}
+
+	public void notifyDomChangeListeners(final IComponentDom dom) {
+		for (final IDomChangeListener listener : domListeners) {
+			listener.domChanged(dom);
+		}
+	}
+
+	public class ParseResult {
+		public final EventBObject astRoot;
+		public final String textInput;
+
+		public ParseResult(final EventBObject astRoot, final String textInput) {
+			this.astRoot = astRoot;
+			this.textInput = textInput;
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/EventDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/EventDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..82d051f4e5dd6b120d2ab8529ead8ce85eab9230
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/EventDom.java
@@ -0,0 +1,71 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Parameter;
+
+public class EventDom extends AbstractDom {
+	private final Event event;
+
+	private final Map<String, Event> refinedEvents = new HashMap<String, Event>();
+	private final Map<String, Parameter> parameters = new HashMap<String, Parameter>();
+
+	public EventDom(final Event event, final MachineDom parent) {
+		super(Type.Event, parent);
+		this.event = event;
+	}
+
+	@Override
+	protected synchronized Set<String> doGetIdentifiers() {
+		final Set<String> result = new HashSet<String>();
+
+		result.addAll(getParameters().keySet());
+
+		return result;
+	}
+
+	@Override
+	protected IdentifierType doGetIdentifierType(final String identifier) {
+		if (parameters.containsKey(identifier)) {
+			return IdentifierType.Parameter;
+		}
+
+		return null;
+	}
+
+	public EventBObject getEventBElement() {
+		return event;
+	}
+
+	public Map<String, Parameter> getParameters() {
+		return parameters;
+	}
+
+	public void addAllParameters(final List<Parameter> params) {
+		for (final Parameter parameter : params) {
+			parameters.put(parameter.getName(), parameter);
+		}
+	}
+
+	public Map<String, Event> getRefinedEvents() {
+		return refinedEvents;
+	}
+
+	public void addAllRefinedEvent(final List<Event> events) {
+		for (final Event evt : events) {
+			refinedEvents.put(evt.getName(), evt);
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/FormulaCollectingSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/FormulaCollectingSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..09c795bf7a2a45e1fe72608e8323baf22f4f6ed7
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/FormulaCollectingSwitch.java
@@ -0,0 +1,52 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import org.eclipse.core.runtime.Assert;
+import org.eventb.emf.formulas.BFormula;
+import org.eventb.emf.formulas.BoundIdentifierExpression;
+import org.eventb.emf.formulas.IdentifierExpression;
+import org.eventb.emf.formulas.util.FormulasSwitch;
+
+public class FormulaCollectingSwitch extends FormulasSwitch<Boolean> {
+
+	private FormulaDom formulaDom;
+	private IDom currentParentDom;
+
+	public void setCurrentParentDom(final IDom currentParentDom) {
+		Assert.isNotNull(currentParentDom);
+		this.currentParentDom = currentParentDom;
+	}
+
+	@Override
+	public Boolean caseBFormula(final BFormula object) {
+		formulaDom = new FormulaDom(object, currentParentDom);
+		currentParentDom.addChild(formulaDom);
+		return true;
+	}
+
+	@Override
+	public Boolean caseIdentifierExpression(final IdentifierExpression object) {
+		if (formulaDom == null) {
+			caseBFormula(object);
+		}
+
+		formulaDom.addFreeIdentifier(object);
+		return false;
+	}
+
+	@Override
+	public Boolean caseBoundIdentifierExpression(
+			final BoundIdentifierExpression object) {
+		if (formulaDom == null) {
+			caseBFormula(object);
+		}
+
+		formulaDom.addBoundIdentifier(object);
+		return false;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/FormulaDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/FormulaDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..4328b5ec101458ffcbe494da941f273241e1ad9a
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/FormulaDom.java
@@ -0,0 +1,67 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.formulas.BFormula;
+import org.eventb.emf.formulas.BoundIdentifierExpression;
+import org.eventb.emf.formulas.IdentifierExpression;
+
+public class FormulaDom extends AbstractDom {
+	private final BFormula formula;
+	private final Map<String, IdentifierExpression> freeIdentifiers = new HashMap<String, IdentifierExpression>();
+	private final Map<String, BoundIdentifierExpression> boundIdentifiers = new HashMap<String, BoundIdentifierExpression>();
+
+	public FormulaDom(final BFormula formula, final IDom parent) {
+		super(Type.Event, parent);
+		this.formula = formula;
+	}
+
+	public EventBObject getEventBElement() {
+		return formula;
+	}
+
+	@Override
+	protected synchronized Set<String> doGetIdentifiers() {
+		final Set<String> result = new HashSet<String>();
+
+		result.addAll(getBoundIdentifiers().keySet());
+		result.addAll(getFreeIdentifiers().keySet());
+
+		return result;
+	}
+
+	@Override
+	protected IdentifierType doGetIdentifierType(final String identifier) {
+		if (boundIdentifiers.containsKey(identifier)) {
+			return IdentifierType.LocalVariable;
+		}
+
+		return null;
+	}
+
+	public Map<String, IdentifierExpression> getFreeIdentifiers() {
+		return freeIdentifiers;
+	}
+
+	public void addFreeIdentifier(final IdentifierExpression expression) {
+		freeIdentifiers.put(expression.getName(), expression);
+	}
+
+	public Map<String, BoundIdentifierExpression> getBoundIdentifiers() {
+		return boundIdentifiers;
+	}
+
+	public void addBoundIdentifier(final BoundIdentifierExpression expression) {
+		boundIdentifiers.put(expression.getName(), expression);
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IComponentDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IComponentDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3340b2aebe181d7cc3b602e8361c4b0f57ac757
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IComponentDom.java
@@ -0,0 +1,22 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.Set;
+
+import org.eclipse.emf.ecore.resource.Resource;
+
+public interface IComponentDom extends IDom {
+
+	public void reset();
+
+	public void resetAndinit();
+
+	public Resource getResource();
+
+	public Set<IComponentDom> getReferencedDoms(final boolean transitive);
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..65828f8998e55190b46a38a0ff1b982c2732f189
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IDom.java
@@ -0,0 +1,38 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.List;
+import java.util.Set;
+
+import org.eventb.emf.core.EventBObject;
+
+public interface IDom {
+	public enum Type {
+		Machine, Context, Event, Formula, Unknown
+	};
+
+	public enum IdentifierType {
+		GlobalVariable, Parameter, Constant, Set, LocalVariable
+	};
+
+	public Type getType();
+
+	public EventBObject getEventBElement();
+
+	public IDom getParent();
+
+	public void addChild(final IDom child);
+
+	public List<IDom> getChildren();
+
+	public IDom getScopingDom(final int offset);
+
+	public Set<String> getIdentifiers();
+
+	public IdentifierType getIdentifierType(String identifier);
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IDomChangeListener.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IDomChangeListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..4fccbbbb54e35bbc6613f78248e622747f6107c9
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IDomChangeListener.java
@@ -0,0 +1,19 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+public interface IDomChangeListener {
+	/**
+	 * Method will be called when a new {@link IDom} is stored in the
+	 * {@link DomManager} for a certain AST. Implementing listeners are asked to
+	 * update according to the new result.
+	 * 
+	 * @param dom
+	 *            The new {@link IDom} or <code>null</code>.
+	 */
+	public void domChanged(final IComponentDom changedDom);
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IParseResultListener.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IParseResultListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..76c32dbd30e35dc5d554a2e9ff9ff56ede3ffee9
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/IParseResultListener.java
@@ -0,0 +1,22 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import org.eclipse.ui.IEditorInput;
+import org.eventb.texteditor.ui.build.dom.DomManager.ParseResult;
+
+public interface IParseResultListener {
+	/**
+	 * Method will be called when a new {@link ParseResult} is stored in the
+	 * {@link DomManager} for a certain {@link IEditorInput}. Implementing
+	 * listeners are asked to update according to the new result.
+	 * 
+	 * @param parseResult
+	 *            The new {@link ParseResult} or <code>null</code>.
+	 */
+	public void parseResultChanged(ParseResult parseResult);
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/MachineCollectingSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/MachineCollectingSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6c275e62db24dc18882d405e7bd2fe59f189b75
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/MachineCollectingSwitch.java
@@ -0,0 +1,101 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.Variable;
+import org.eventb.emf.core.machine.util.MachineSwitch;
+
+public class MachineCollectingSwitch extends MachineSwitch<Boolean> {
+	private final MachineDom machineDom;
+	private IDom currentParent = null;
+	private final Resource machineResource;
+
+	protected MachineCollectingSwitch(final MachineDom dom,
+			final Resource machineResource) {
+		machineDom = dom;
+		this.machineResource = machineResource;
+	}
+
+	protected AbstractDom getDom() {
+		return machineDom;
+	}
+
+	public IDom getCurrentParentDom() {
+		return currentParent;
+	}
+
+	@Override
+	public Boolean caseEventBObject(final EventBObject object) {
+		return true;
+	}
+
+	@Override
+	public Boolean caseMachine(final Machine object) {
+		machineDom.setMachine(object);
+
+		final EList<Machine> refines = object.getRefines();
+		for (final Machine machine : refines) {
+			final IComponentDom dom = DomBuilder.getReferencedDom(machine,
+					machineResource);
+
+			if (dom != null) {
+				Assert.isTrue(dom instanceof MachineDom);
+				machineDom.addRefinedMachine((MachineDom) dom);
+			}
+
+			/*
+			 * If no DOM is found the reference must be to a not-existing
+			 * machine.
+			 */
+		}
+
+		final EList<Context> sees = object.getSees();
+		for (final Context context : sees) {
+			final IComponentDom dom = DomBuilder.getReferencedDom(context,
+					machineResource);
+
+			if (dom != null) {
+				Assert.isTrue(dom instanceof ContextDom);
+				machineDom.addSeenContext((ContextDom) dom);
+			}
+
+			/*
+			 * If no DOM is found the reference must be to a not-existing
+			 * machine.
+			 */
+		}
+
+		currentParent = machineDom;
+
+		return true;
+	}
+
+	@Override
+	public Boolean caseVariable(final Variable object) {
+		machineDom.addVariable(object);
+		return false;
+	}
+
+	@Override
+	public Boolean caseEvent(final Event object) {
+		final EventDom eventDom = new EventDom(object, machineDom);
+		eventDom.addAllRefinedEvent(object.getRefines());
+		eventDom.addAllParameters(object.getParameters());
+		machineDom.addChild(eventDom);
+
+		currentParent = eventDom;
+
+		return true;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/MachineDom.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/MachineDom.java
new file mode 100644
index 0000000000000000000000000000000000000000..041ac774e17b54dc861fc1834e42b126f7d66b32
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/build/dom/MachineDom.java
@@ -0,0 +1,169 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.build.dom;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.CarrierSet;
+import org.eventb.emf.core.context.Constant;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.Variable;
+
+public class MachineDom extends AbstractComponentDom {
+	private Machine machine;
+	private final Map<String, Variable> variables = new HashMap<String, Variable>();
+
+	private final List<MachineDom> refinedMachines = new LinkedList<MachineDom>();
+	private final List<ContextDom> seenContexts = new LinkedList<ContextDom>();
+
+	MachineDom(final Resource resource, final Machine machine) {
+		super(Type.Machine, null, resource);
+		this.machine = machine;
+	}
+
+	@Override
+	protected synchronized void doReset() {
+		refinedMachines.clear();
+		seenContexts.clear();
+		variables.clear();
+	}
+
+	@Override
+	protected synchronized Set<String> doGetIdentifiers() {
+		final Set<String> result = new HashSet<String>();
+
+		result.addAll(getVariables(true).keySet());
+		result.addAll(getConstants().keySet());
+		result.addAll(getSets().keySet());
+
+		return result;
+	}
+
+	@Override
+	protected synchronized IdentifierType doGetIdentifierType(
+			final String identifier) {
+		if (variables.containsKey(identifier)) {
+			return IdentifierType.GlobalVariable;
+		}
+
+		if (getConstants().containsKey(identifier)) {
+			return IdentifierType.Constant;
+		}
+
+		if (getSets().containsKey(identifier)) {
+			return IdentifierType.Set;
+		}
+
+		return null;
+	}
+
+	public Set<IComponentDom> getReferencedDoms(final boolean transitive) {
+		final List<ContextDom> contexts = getSeenContexts();
+		final List<MachineDom> machines = getRefinedMachines();
+		final HashSet<IComponentDom> result = new HashSet<IComponentDom>(
+				contexts);
+		result.addAll(machines);
+
+		if (transitive) {
+			for (final IComponentDom referencedDom : contexts) {
+				result.addAll(referencedDom.getReferencedDoms(transitive));
+			}
+
+			for (final IComponentDom referencedDom : machines) {
+				result.addAll(referencedDom.getReferencedDoms(false));
+			}
+		}
+
+		return result;
+	}
+
+	public synchronized Map<String, Variable> getVariables(
+			final boolean includeInherited) {
+		checkInitialization();
+
+		final Map<String, Variable> result = new HashMap<String, Variable>();
+
+		if (includeInherited) {
+			// add referenced machine's variables
+			for (final MachineDom dom : getRefinedMachines()) {
+				/*
+				 * Only add directly referenced machine's variables, but not
+				 * transitively. Variables need to be redeclared then.
+				 */
+				result.putAll(dom.getVariables(false));
+			}
+		}
+
+		// finally the local variables
+		result.putAll(variables);
+
+		return result;
+	}
+
+	public synchronized Map<String, Constant> getConstants() {
+		checkInitialization();
+
+		final Map<String, Constant> result = new HashMap<String, Constant>();
+
+		// add referenced context's constants
+		for (final ContextDom dom : getSeenContexts()) {
+			result.putAll(dom.getConstants(true));
+		}
+
+		return result;
+	}
+
+	public synchronized Map<String, CarrierSet> getSets() {
+		checkInitialization();
+
+		final Map<String, CarrierSet> result = new HashMap<String, CarrierSet>();
+
+		// add referenced context's constants
+		for (final ContextDom dom : getSeenContexts()) {
+			result.putAll(dom.getSets(true));
+		}
+
+		return result;
+	}
+
+	public synchronized List<MachineDom> getRefinedMachines() {
+		checkInitialization();
+		return refinedMachines;
+	}
+
+	public synchronized List<ContextDom> getSeenContexts() {
+		checkInitialization();
+		return seenContexts;
+	}
+
+	synchronized void addVariable(final Variable var) {
+		variables.put(var.getName(), var);
+	}
+
+	synchronized void addRefinedMachine(final MachineDom dom) {
+		refinedMachines.add(dom);
+	}
+
+	synchronized void addSeenContext(final ContextDom dom) {
+		seenContexts.add(dom);
+	}
+
+	public synchronized EventBObject getEventBElement() {
+		return machine;
+	}
+
+	public void setMachine(final Machine machine) {
+		this.machine = machine;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/AnnotationModel.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/AnnotationModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..e89878fbeebfb429a0bcb8185c14537b74245cef
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/AnnotationModel.java
@@ -0,0 +1,225 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
+import org.eventb.core.IExtendsContext;
+import org.eventb.core.IRefinesEvent;
+import org.eventb.core.IRefinesMachine;
+import org.eventb.core.ISeesContext;
+import org.eventb.emf.core.EventBExpression;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.EventBPredicate;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Parameter;
+import org.eventb.emf.core.machine.Variable;
+import org.eventb.emf.persistence.factory.RodinResource;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.model.texttools.TextRange;
+import org.rodinp.core.IInternalElement;
+import org.rodinp.core.RodinDBException;
+import org.rodinp.core.RodinMarkerUtil;
+
+public class AnnotationModel extends ResourceMarkerAnnotationModel {
+	private static final String LABEL_ATTRIBUTE_ID = "org.eventb.core.label";
+	private final RodinResource rodinResource;
+
+	public AnnotationModel(final IResource fileResource,
+			final Resource emfResource) {
+		super(fileResource);
+
+		if (emfResource instanceof RodinResource) {
+			rodinResource = (RodinResource) emfResource;
+		} else {
+			rodinResource = null;
+		}
+	}
+
+	@Override
+	protected Position createPositionFromMarker(final IMarker marker) {
+		try {
+			if (RodinMarkerUtil.RODIN_PROBLEM_MARKER.equals(marker.getType())) {
+				return createPosition(marker);
+			}
+		} catch (final CoreException e) {
+			TextEditorPlugin.getPlugin().getLog().log(
+					new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID,
+							"Error analyzing marker", e));
+		}
+
+		return super.createPositionFromMarker(marker);
+	}
+
+	private Position createPosition(final IMarker marker) {
+		final IRegion region = getBestRegion(marker);
+
+		if (region != null) {
+			return new Position(region.getOffset(), region.getLength());
+		} else {
+			return null;
+		}
+	}
+
+	private IRegion getBestRegion(final IMarker marker) {
+		final int charStart = RodinMarkerUtil.getCharStart(marker);
+		final int charEnd = RodinMarkerUtil.getCharEnd(marker);
+
+		final IInternalElement rodinElement = RodinMarkerUtil
+				.getInternalElement(marker);
+
+		// special case for references (sees, refines, ...)
+		if (isReferenceElement(rodinElement)) {
+			return handleReferenceElement(rodinElement);
+		}
+
+		final EventBObject eventBObject = rodinResource.getMap().get(
+				rodinElement);
+
+		if (charStart >= 0 && charEnd >= 0) {
+			/*
+			 * If start and end char is set, it must be a problem inside a
+			 * formula.
+			 */
+			return getRegionInFormula(eventBObject, charStart, charEnd);
+
+		} else if (onlyLabelIsRelevant(marker, eventBObject)) {
+			final TextRange range = TextPositionUtil.getInternalPosition(
+					eventBObject, ((EventBNamed) eventBObject).getName());
+
+			if (range != null) {
+				return new Region(range.getOffset(), range.getLength());
+			}
+		} else {
+			/*
+			 * Asuming the whole element has a problem as a fallback
+			 */
+			final TextRange range = TextPositionUtil.getTextRange(eventBObject);
+
+			if (range != null) {
+				return new Region(range.getOffset(), range.getLength());
+			}
+		}
+
+		return new Region(0, 0);
+	}
+
+	private boolean isReferenceElement(final IInternalElement rodinElement) {
+		return rodinElement instanceof IRefinesEvent
+				|| rodinElement instanceof ISeesContext
+				|| rodinElement instanceof IRefinesMachine
+				|| rodinElement instanceof IExtendsContext;
+	}
+
+	private IRegion handleReferenceElement(final IInternalElement rodinElement) {
+		final EventBObject parent = rodinResource.getMap().get(
+				rodinElement.getParent());
+
+		if (parent != null) {
+			String name = null;
+
+			try {
+				if (rodinElement instanceof IRefinesEvent) {
+					name = ((IRefinesEvent) rodinElement)
+							.getAbstractEventLabel();
+				} else if (rodinElement instanceof ISeesContext) {
+					name = ((ISeesContext) rodinElement).getSeenContextName();
+				} else if (rodinElement instanceof IRefinesMachine) {
+					name = ((IRefinesMachine) rodinElement)
+							.getAbstractMachineName();
+				} else if (rodinElement instanceof IExtendsContext) {
+					name = ((IExtendsContext) rodinElement)
+							.getAbstractContextName();
+				}
+
+				final TextRange range = TextPositionUtil.getInternalPosition(
+						parent, name);
+				if (range != null) {
+					return new Region(range.getOffset(), range.getLength());
+				}
+			} catch (final RodinDBException e) {
+				// IGNORE
+			}
+		}
+
+		return new Region(0, 0);
+	}
+
+	private boolean onlyLabelIsRelevant(final IMarker marker,
+			final EventBObject eventBObject) {
+		/*
+		 * The attribute means there's a problem with the label of this element.
+		 * Of course this needs to be an EventBNamed element too.
+		 */
+		final String attributeId = marker.getAttribute(
+				RodinMarkerUtil.ATTRIBUTE_ID, null);
+		if (LABEL_ATTRIBUTE_ID.equals(attributeId)
+				&& eventBObject instanceof EventBNamed) {
+			return true;
+		}
+
+		/*
+		 * If the surrounding element is an event we don't want to highlight the
+		 * whole event. This could be very distracting to the user. So we just
+		 * choose the event's name.
+		 */
+		if (eventBObject instanceof Event) {
+			return true;
+		}
+
+		/*
+		 * When the problem is in a variable or parameter declaration we want
+		 * only the identifier to be highlighted but not optional comments.
+		 */
+		if (eventBObject instanceof Variable
+				|| eventBObject instanceof Parameter) {
+			return true;
+		}
+
+		return false;
+	}
+
+	private IRegion getRegionInFormula(final EventBObject eventBObject,
+			final int charStart, final int charEnd) {
+		TextRange range = null;
+
+		if (eventBObject instanceof EventBPredicate) {
+			range = TextPositionUtil.getInternalPosition(eventBObject,
+					((EventBPredicate) eventBObject).getPredicate());
+		} else if (eventBObject instanceof Action) {
+			range = TextPositionUtil.getInternalPosition(eventBObject,
+					((Action) eventBObject).getAction());
+		} else if (eventBObject instanceof EventBExpression) {
+			range = TextPositionUtil.getInternalPosition(eventBObject,
+					((EventBExpression) eventBObject).getExpression());
+		}
+
+		if (range != null) {
+			return new Region(range.getOffset() + charStart, charEnd
+					- charStart);
+		} else {
+			// fall back and mark the complete formula
+			range = TextPositionUtil.getTextRange(eventBObject);
+			if (range != null) {
+				return new Region(range.getOffset(), range.getLength());
+			}
+		}
+
+		return null;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/DocumentProvider.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/DocumentProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..744ed749b5bc66a4a92ca328120fc6008f8d2322
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/DocumentProvider.java
@@ -0,0 +1,273 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.edit.ui.util.EditUIUtil;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.editors.text.FileDocumentProvider;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.build.Builder;
+import org.eventb.texteditor.ui.build.dom.DomManager.ParseResult;
+import org.eventb.texteditor.ui.reconciler.partitioning.PartitionScanner;
+import org.eventb.texteditor.ui.reconciler.partitioning.Partitioner;
+import org.eventb.texttools.PersistenceHelper;
+
+public class DocumentProvider extends FileDocumentProvider {
+
+	private final EventBTextEditor editor;
+
+	public DocumentProvider(final EventBTextEditor editor) {
+		this.editor = editor;
+	}
+
+	@Override
+	protected IAnnotationModel createAnnotationModel(final Object element)
+			throws CoreException {
+		if (element instanceof IFileEditorInput) {
+			final IFileEditorInput input = (IFileEditorInput) element;
+			return new AnnotationModel(input.getFile(),
+					getResourceForElement(input));
+		}
+
+		return super.createAnnotationModel(element);
+	}
+
+	@Override
+	protected void setupDocument(final Object element, final IDocument document) {
+		// remove old partitioner if set
+		final IDocumentPartitioner oldPartitioner = document
+				.getDocumentPartitioner();
+		if (oldPartitioner != null) {
+			oldPartitioner.disconnect();
+		}
+
+		final Partitioner partitioner = new Partitioner(new PartitionScanner(),
+				PartitionScanner.CONTENT_TYPES);
+		document.setDocumentPartitioner(partitioner);
+		partitioner.connect(document, true);
+	}
+
+	@Override
+	protected ElementInfo createElementInfo(final Object element)
+			throws CoreException {
+		final ElementInfo info = super.createElementInfo(element);
+
+		if (info != null && element instanceof IEditorInput) {
+			final IDocument document = info.fDocument;
+
+			document.addDocumentListener(new IDocumentListener() {
+				public void documentChanged(final DocumentEvent event) {
+					final boolean newState = isDirty(info,
+							(IEditorInput) element);
+
+					if (info.fCanBeSaved != newState) {
+						info.fCanBeSaved = newState;
+						editor.updateIsDirty();
+					}
+				}
+
+				public void documentAboutToBeChanged(final DocumentEvent event) {
+					// IGNORE
+				}
+			});
+		}
+		return info;
+	}
+
+	private boolean isDirty(final ElementInfo info, final IEditorInput element) {
+		final String currentInput = info.fDocument.get();
+		final String savedInput = PersistenceHelper
+				.getTextAnnotation(getResourceForElement(element));
+
+		return currentInput != null && !currentInput.equals(savedInput);
+	}
+
+	@Override
+	protected boolean setDocumentContent(final IDocument document,
+			final IEditorInput editorInput, final String encoding)
+			throws CoreException {
+		/*
+		 * We intercept loading from file and replace it with loading from
+		 * RodinDB plus conversion to text (if necessary).
+		 */
+		final Resource resource = getResourceForElement(editorInput);
+
+		// load content as stream
+		InputStream stream;
+		try {
+			stream = createContentStream(resource, encoding, document
+					.getLegalLineDelimiters()[0]);
+		} catch (final IOException e) {
+			// IGNORE
+			return false;
+		}
+
+		try {
+			setDocumentContent(document, stream, encoding);
+
+			/*
+			 * After setting content in new document initially parse and merge
+			 * result into original model to create text positions in the
+			 * original model.
+			 */
+			reconcileContent(resource, document, null);
+			return true;
+		} finally {
+			try {
+				if (stream != null) {
+					stream.close();
+				}
+			} catch (final IOException ex) {
+				// IGNORE, no solution available here
+			}
+		}
+	}
+
+	@Override
+	protected ISchedulingRule getSaveRule(final Object element) {
+		/*
+		 * We need to set the scheduling rule to the workspace root. Save
+		 * operations of the editor run in a runnable and its scope is
+		 * determined with the help of this method. The EMF persistence layer
+		 * wrappes a save into another runnable which is handled by the RodinDB.
+		 * The DB uses the workspace root as scope. If we set a smaller scope
+		 * this collides with the larger scope.
+		 */
+		if (element instanceof IFileEditorInput) {
+			return ResourcesPlugin.getWorkspace().getRoot();
+		}
+
+		return null;
+	}
+
+	@Override
+	protected void doSaveDocument(final IProgressMonitor monitor,
+			final Object element, final IDocument document,
+			final boolean overwrite) throws CoreException {
+
+		if (element instanceof IEditorInput) {
+			final SubMonitor subMonitor = SubMonitor.convert(monitor, "Saving",
+					4);
+
+			editor.setIsSaving(true);
+			final Resource resource = getResourceForElement((IEditorInput) element);
+
+			// trim lines to remove trailing whitespaces
+			final int newCursorOffset = trimLines(document, subMonitor
+					.newChild(1));
+
+			// parse and translate formulas
+			reconcileContent(resource, document, subMonitor.newChild(1));
+			subMonitor.worked(1);
+
+			PersistenceHelper.saveText(resource, overwrite, subMonitor
+					.newChild(1));
+
+			editor.setIsSaving(false);
+			editor.setCurserOffset(newCursorOffset);
+			editor.selectAndReveal(newCursorOffset, 0);
+		} else {
+			throw new CoreException(new Status(IStatus.ERROR,
+					TextEditorPlugin.PLUGIN_ID, "Unsupported target element: "
+							+ element));
+		}
+	}
+
+	private void reconcileContent(final Resource resource,
+			final IDocument document, final IProgressMonitor m) {
+		final SubMonitor monitor = SubMonitor.convert(m, 2);
+		EventBNamedCommentedComponentElement astRoot = null;
+
+		final Builder builder = new Builder();
+		builder.run(editor, document, monitor.newChild(1));
+
+		if (builder.wasSuccessful()) {
+			final ParseResult lastParseResult = TextEditorPlugin
+					.getDomManager()
+					.getLastParseResult(editor.getEditorInput());
+
+			if (lastParseResult != null) {
+				astRoot = (EventBNamedCommentedComponentElement) lastParseResult.astRoot;
+			}
+		}
+
+		if (astRoot != null) {
+			PersistenceHelper.mergeRootElement(resource, astRoot, monitor
+					.newChild(1));
+		} else {
+			// we need to store the current text because it is not parsable
+			try {
+				PersistenceHelper.addTextAnnotation(resource, document.get(),
+						System.currentTimeMillis());
+			} catch (final CoreException e) {
+				TextEditorPlugin
+						.getPlugin()
+						.getLog()
+						.log(
+								new Status(
+										IStatus.ERROR,
+										TextEditorPlugin.PLUGIN_ID,
+										"Cannot store text representation into Resource",
+										e));
+			}
+
+			monitor.worked(1);
+		}
+	}
+
+	private int trimLines(final IDocument document, final SubMonitor monitor) {
+		final SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
+		int cursorOffset = editor.getCursorOffset();
+
+		try {
+			final int removedChars = TrimLinesAction.trimLines(document, 0,
+					document.getNumberOfLines() - 1, subMonitor.newChild(1));
+			cursorOffset -= removedChars;
+
+			return cursorOffset < document.getLength() ? cursorOffset
+					: document.getLength() - 1;
+		} catch (final BadLocationException e) {
+			// IGNORE
+			return cursorOffset;
+		}
+	}
+
+	private Resource getResourceForElement(final IEditorInput editorInput) {
+		final URI inputUri = EditUIUtil.getURI(editorInput);
+		final Resource resource = editor.getEditingDomain().getResourceSet()
+				.getResource(inputUri, true);
+		return resource;
+	}
+
+	private InputStream createContentStream(final Resource resource,
+			final String encoding, final String lineBreak)
+			throws CoreException, IOException {
+		return new ByteArrayInputStream(PersistenceHelper.loadText(resource,
+				lineBreak).getBytes(encoding));
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/EventBTextEditor.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/EventBTextEditor.java
new file mode 100644
index 0000000000000000000000000000000000000000..86834d3fdbfc45d8ad8516c9e48932b9aafd1a34
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/EventBTextEditor.java
@@ -0,0 +1,407 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
+import org.eclipse.emf.edit.ui.util.EditUIUtil;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.link.LinkedModeUI;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.LineChangeHover;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.ide.IGotoMarker;
+import org.eclipse.ui.texteditor.ContentAssistAction;
+import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
+import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+import org.eventb.emf.core.context.ContextPackage;
+import org.eventb.emf.core.machine.MachinePackage;
+import org.eventb.texteditor.ui.Images;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.outline.OutlinePage;
+import org.eventb.texteditor.ui.outline.RevealSelectionListener;
+import org.eventb.texteditor.ui.reconciler.EventBPresentationReconciler;
+import org.eventb.texttools.PersistenceHelper;
+import org.eventb.texttools.TextToolsPlugin;
+
+public class EventBTextEditor extends TextEditor implements IGotoMarker {
+
+	public static final String CONTENT_FORMAT = "ContentFormat";
+	public static final String CONTENT_ASSIST_PROPOSAL = "ContentAssistProposal";
+
+	private GotoMarker gotoMarkerAdapter;
+	private IContentOutlinePage contentOutlinePage;
+	private Resource resource;
+	private ModelChangeListener modelChangeListener;
+	private AdapterFactoryEditingDomain editingDomain;
+	private boolean isCurrentlySaving = false;
+	private EventBPresentationReconciler presentationReconciler;
+	private final IPartListener activeListener;
+
+	private boolean resourceChangedWhileDirty;
+
+	private boolean inLinkedMode = false;
+
+	public EventBTextEditor() {
+		activeListener = new ActivateListener();
+	}
+
+	@Override
+	public void init(final IEditorSite site, final IEditorInput input)
+			throws PartInitException {
+		super.init(site, input);
+		site.getPage().addPartListener(activeListener);
+	}
+
+	@Override
+	protected void initializeEditor() {
+		super.initializeEditor();
+
+		setDocumentProvider(new DocumentProvider(this));
+		final SourceViewerConfiguration viewerConfiguration = new SourceViewerConfiguration(
+				this, getPreferenceStore());
+		presentationReconciler = (EventBPresentationReconciler) viewerConfiguration
+				.getPresentationReconciler(getSourceViewer());
+		setSourceViewerConfiguration(viewerConfiguration);
+
+		// not supported for our editor at the moment
+		showChangeInformation(false);
+
+		setEditorContextMenuId(TextEditorPlugin.TEXTEDITOR_CONTEXTMENU_ID);
+	}
+
+	@Override
+	protected void createActions() {
+		super.createActions();
+
+		/*
+		 * Add content assist
+		 */
+		final Action action = new ContentAssistAction(TextEditorPlugin
+				.getPlugin().getResourceBundle(), "ContentAssistProposal.",
+				this);
+		action
+				.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
+		setAction(CONTENT_ASSIST_PROPOSAL, action);
+		markAsStateDependentAction(CONTENT_ASSIST_PROPOSAL, true);
+	}
+
+	@Override
+	protected void doSetInput(final IEditorInput input) throws CoreException {
+		final IEditorInput oldInput = getEditorInput();
+
+		super.doSetInput(input);
+		resource = null;
+
+		registerChangeListener(oldInput, getEditorInput());
+		presentationReconciler.setInputResource(getResource());
+
+		adjustLabels();
+	}
+
+	private void registerChangeListener(final IEditorInput oldInput,
+			final IEditorInput editorInput) {
+		if (modelChangeListener != null && oldInput instanceof IFileEditorInput) {
+			((IFileEditorInput) oldInput).getFile().getWorkspace()
+					.removeResourceChangeListener(modelChangeListener);
+		}
+
+		if (editorInput instanceof IFileEditorInput) {
+			modelChangeListener = new ModelChangeListener(this);
+			((IFileEditorInput) editorInput).getFile().getWorkspace()
+					.addResourceChangeListener(modelChangeListener);
+		}
+	}
+
+	public AdapterFactoryEditingDomain getEditingDomain() {
+		if (editingDomain == null) {
+			final IFileEditorInput fileInput = (IFileEditorInput) getEditorInput();
+			final IProject project = fileInput.getFile().getProject();
+
+			editingDomain = TextToolsPlugin.getDefault().getResourceManager()
+					.getEditingDomain(project);
+		}
+
+		return editingDomain;
+	}
+
+	@Override
+	protected String[] collectContextMenuPreferencePages() {
+		final List<String> result = new ArrayList<String>(Arrays.asList(super
+				.collectContextMenuPreferencePages()));
+		result.add("org.eventb.texteditor.ui.preferences");
+		result
+				.add("org.eventb.texteditor.ui.preferences.HighlightingPreferencePage");
+		result.add("org.eventb.texteditor.ui.preferences.TemplatePreferences");
+
+		return result.toArray(new String[result.size()]);
+	}
+
+	@Override
+	protected LineChangeHover createChangeHover() {
+		// disabling change hover because we don't support it (yet)
+		return null;
+	}
+
+	@Override
+	public boolean isSaveAsAllowed() {
+		return false;
+	}
+
+	@Override
+	public void doSaveAs() {
+		throw new UnsupportedOperationException(
+				"'SaveAs' is not allowed for the Event-B text editor.");
+	}
+
+	@Override
+	public void dispose() {
+		getSite().getPage().removePartListener(activeListener);
+		registerChangeListener(getEditorInput(), null);
+		super.dispose();
+	}
+
+	public Resource getResource() {
+		if (resource == null) {
+			final IEditorInput editorInput = getEditorInput();
+			if (editorInput != null) {
+				final AdapterFactoryEditingDomain editingDomain = getEditingDomain();
+				final URI inputUri = EditUIUtil.getURI(editorInput);
+				resource = editingDomain.getResourceSet().getResource(inputUri,
+						true);
+			}
+		}
+
+		return resource;
+	}
+
+	public StyledText getTextWidget() {
+		return getSourceViewer().getTextWidget();
+	}
+
+	public void insert(final String symbol, final boolean correctCursorPosition) {
+		if (symbol != null && symbol.length() > 0) {
+			getSite().getShell().getDisplay().asyncExec(new Runnable() {
+				public void run() {
+					final IDocument document = getDocumentProvider()
+							.getDocument(getEditorInput());
+					final int offset = getCursorOffset();
+
+					try {
+						document.replace(offset, 0, symbol);
+
+						if (correctCursorPosition) {
+							setCurserOffset(offset + symbol.length());
+						}
+					} catch (final BadLocationException e) {
+						TextEditorPlugin
+								.getPlugin()
+								.getLog()
+								.log(
+										new Status(
+												IStatus.WARNING,
+												TextEditorPlugin.PLUGIN_ID,
+												"Could not insert text into Event-B text editor.",
+												e));
+					}
+				}
+			});
+		}
+	}
+
+	public int getCursorOffset() {
+		final ISourceViewer sourceViewer = getSourceViewer();
+		if (sourceViewer != null) {
+			return widgetOffset2ModelOffset(sourceViewer, sourceViewer
+					.getTextWidget().getCaretOffset());
+		}
+
+		return 0;
+	}
+
+	public void setCurserOffset(final int offsetInDocument) {
+		final ISourceViewer sourceViewer = getSourceViewer();
+		if (sourceViewer != null) {
+			final int widgetOffset = modelOffset2WidgetOffset(sourceViewer,
+					offsetInDocument);
+			final StyledText textWidget = sourceViewer.getTextWidget();
+			textWidget.setCaretOffset(widgetOffset);
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public Object getAdapter(final Class adapter) {
+		if (IGotoMarker.class.equals(adapter)) {
+			return getGotoMarker();
+		}
+
+		if (adapter.equals(IContentOutlinePage.class)) {
+			return getContentOutlinePage();
+		}
+
+		return super.getAdapter(adapter);
+	}
+
+	private Object getGotoMarker() {
+		if (gotoMarkerAdapter == null) {
+			gotoMarkerAdapter = new GotoMarker(this);
+		}
+
+		return gotoMarkerAdapter;
+	}
+
+	private IContentOutlinePage getContentOutlinePage() {
+		if (contentOutlinePage == null) {
+			contentOutlinePage = new OutlinePage(this);
+			contentOutlinePage
+					.addSelectionChangedListener(new RevealSelectionListener(
+							this));
+		}
+
+		return contentOutlinePage;
+	}
+
+	public void updateIsDirty() {
+		firePropertyChange(PROP_DIRTY);
+	}
+
+	public void setIsSaving(final boolean state) {
+		isCurrentlySaving = state;
+	}
+
+	public boolean isSaving() {
+		return isCurrentlySaving;
+	}
+
+	public final int widgetOffset2ModelOffset(final int widgetOffset) {
+		return widgetOffset2ModelOffset(getSourceViewer(), widgetOffset);
+	}
+
+	public final int modelOffset2WidgetOffset(final int modelOffset) {
+		return modelOffset2WidgetOffset(getSourceViewer(), modelOffset);
+	}
+
+	public EventBPresentationReconciler getPresentationReconciler() {
+		return presentationReconciler;
+	}
+
+	public void changeWhileDirty() {
+		resourceChangedWhileDirty = true;
+	}
+
+	/**
+	 * Change whether the editor is currently in 'linked mode'.
+	 * 
+	 * @param linkedMode
+	 */
+	public void setInLinkedMode(final boolean linkedMode) {
+		inLinkedMode = linkedMode;
+	}
+
+	/**
+	 * Returns whether the editor is currently in 'linked mode', i.e., when a
+	 * template has just been inserted.
+	 * 
+	 * @see LinkedModeUI
+	 * @return
+	 */
+	public boolean isInLinkedMode() {
+		return inLinkedMode;
+	}
+
+	private void adjustLabels() {
+		final IEditorInput input = getEditorInput();
+
+		if (input != null) {
+			final URI inputUri = EditUIUtil.getURI(input);
+			final Resource resource = getEditingDomain().getResourceSet()
+					.getResource(inputUri, true);
+
+			changeImage(resource);
+
+			if (input instanceof IFileEditorInput) {
+				final IFile file = ((IFileEditorInput) input).getFile();
+				String name = file.getName();
+				name = name.substring(0, name.length()
+						- file.getFileExtension().length() - 1);
+				setPartName(name);
+			}
+		}
+	}
+
+	private void changeImage(final Resource resource) {
+		final EClass componentType = PersistenceHelper
+				.getComponentType(resource);
+		if (componentType != null) {
+			final String packageNsURI = componentType.getEPackage().getNsURI();
+			if (packageNsURI.equals(MachinePackage.eNS_URI)) {
+				setTitleImage(Images.getImage(Images.IMG_MACHINE));
+			} else if (packageNsURI.equals(ContextPackage.eNS_URI)) {
+				setTitleImage(Images.getImage(Images.IMG_CONTEXT));
+			}
+		}
+	}
+
+	private void handleActive() {
+		if (resourceChangedWhileDirty) {
+			try {
+				handleEditorInputChanged();
+			} finally {
+				resourceChangedWhileDirty = false;
+			}
+		}
+
+		// only update presentation
+		presentationReconciler.reconcilePresentation();
+	}
+
+	class ActivateListener implements IPartListener {
+
+		public void partActivated(final IWorkbenchPart part) {
+			if (part == EventBTextEditor.this) {
+				handleActive();
+			}
+		}
+
+		public void partBroughtToTop(final IWorkbenchPart part) {
+			// IGNORE
+		}
+
+		public void partClosed(final IWorkbenchPart part) {
+			// IGNORE
+		}
+
+		public void partDeactivated(final IWorkbenchPart part) {
+			// IGNORE
+		}
+
+		public void partOpened(final IWorkbenchPart part) {
+			// IGNORE
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/GotoMarker.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/GotoMarker.java
new file mode 100644
index 0000000000000000000000000000000000000000..fad681f99575f2f41917c7e8785240c9bc383387
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/GotoMarker.java
@@ -0,0 +1,47 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.ide.IGotoMarker;
+import org.eclipse.ui.texteditor.MarkerUtilities;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.rodinp.core.RodinMarkerUtil;
+
+public class GotoMarker implements IGotoMarker {
+
+	private final EventBTextEditor editor;
+
+	public GotoMarker(final EventBTextEditor editor) {
+		this.editor = editor;
+	}
+
+	public void gotoMarker(final IMarker marker) {
+		/*
+		 * Unfortunately the marker can already be gone, i.e., not exiting by
+		 * the time we are called. This happens, for example, when the
+		 * corresponding editor needs to opened before and starts an initial
+		 * reconcile which replaces the markers.
+		 */
+		if (marker.exists()) {
+			try {
+				if (TextEditorPlugin.SYNTAXERROR_MARKER_ID.equals(marker.getType())) {
+					final int charStart = MarkerUtilities.getCharStart(marker);
+					final int charEnd = MarkerUtilities.getCharEnd(marker);
+
+					editor.selectAndReveal(charStart, charEnd - charStart);
+				} else if (RodinMarkerUtil.RODIN_PROBLEM_MARKER.equals(marker
+						.getType())) {
+					// TODO find a way to locate the problem in our text
+				}
+			} catch (final CoreException exception) {
+				TextEditorPlugin.INSTANCE.log(exception);
+			}
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/ModelChangeListener.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/ModelChangeListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b2b4fbc504aa300a86c45f8cb1bf5491230039f
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/ModelChangeListener.java
@@ -0,0 +1,105 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+
+public class ModelChangeListener implements IResourceChangeListener,
+		IResourceDeltaVisitor {
+
+	private final EventBTextEditor editor;
+
+	public ModelChangeListener(final EventBTextEditor editor) {
+		this.editor = editor;
+	}
+
+	/*
+	 * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
+	 */
+	public void resourceChanged(final IResourceChangeEvent e) {
+		final IResourceDelta delta = e.getDelta();
+		try {
+			if (delta != null) {
+				delta.accept(this);
+			}
+		} catch (final CoreException x) {
+			// TODO log exception
+		}
+	}
+
+	/*
+	 * @see
+	 * IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
+	 */
+	public boolean visit(IResourceDelta delta) throws CoreException {
+		if (delta == null) {
+			return false;
+		}
+
+		final IFile file = getFile();
+		if (file == null) {
+			return false;
+		}
+
+		delta = delta.findMember(file.getFullPath());
+
+		if (delta == null) {
+			return false;
+		}
+
+		if (delta.getKind() == IResourceDelta.CHANGED) {
+			// is change affecting content?
+			if ((IResourceDelta.CONTENT & delta.getFlags()) != 0) {
+				// has editor unsaved changes?
+				if (editor.isDirty()) {
+					// notify editor about change
+					editor.changeWhileDirty();
+				}
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Computes the initial modification stamp for the given resource.
+	 * 
+	 * @param resource
+	 *            the resource
+	 * @return the modification stamp
+	 */
+	protected long computeModificationStamp(final IResource resource) {
+		long modificationStamp = resource.getModificationStamp();
+
+		final IPath path = resource.getLocation();
+		if (path == null) {
+			return modificationStamp;
+		}
+
+		modificationStamp = path.toFile().lastModified();
+		return modificationStamp;
+	}
+
+	private IFile getFile() {
+		final IEditorInput editorInput = editor.getEditorInput();
+
+		if (editorInput instanceof IFileEditorInput) {
+			return ((IFileEditorInput) editorInput).getFile();
+		}
+
+		return null;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/SourceViewerConfiguration.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/SourceViewerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..29ba76bbd50375bc02e5c4bf469bc7cc38872d87
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/SourceViewerConfiguration.java
@@ -0,0 +1,88 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ContentAssistant;
+import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
+import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.presentation.IPresentationReconciler;
+import org.eclipse.jface.text.reconciler.IReconciler;
+import org.eclipse.jface.text.reconciler.MonoReconciler;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
+import org.eventb.texteditor.ui.build.ReconcilingStrategy;
+import org.eventb.texteditor.ui.editor.codecompletion.DefaultContentAssist;
+import org.eventb.texteditor.ui.reconciler.EventBPresentationReconciler;
+import org.eventb.texteditor.ui.reconciler.partitioning.PartitionScanner;
+
+public class SourceViewerConfiguration extends TextSourceViewerConfiguration {
+
+	private final EventBTextEditor editor;
+	private EventBPresentationReconciler presentationReconciler;
+
+	public SourceViewerConfiguration(final EventBTextEditor editor,
+			final IPreferenceStore preferenceStore) {
+		super(preferenceStore);
+		this.editor = editor;
+	}
+
+	@Override
+	public IContentAssistant getContentAssistant(
+			final ISourceViewer sourceViewer) {
+		final ContentAssistant assistant = new ContentAssistant();
+		final IContentAssistProcessor processor = new DefaultContentAssist(
+				editor);
+
+		assistant.setContentAssistProcessor(processor,
+				IDocument.DEFAULT_CONTENT_TYPE);
+		assistant.setContentAssistProcessor(processor,
+				PartitionScanner.CONTENT_TYPE_STRUCTURAL_KEYWORD);
+		assistant.setContentAssistProcessor(processor,
+				PartitionScanner.CONTENT_TYPE_FORMULA_KEYWORD);
+
+		assistant
+				.setInformationControlCreator(getInformationControlCreator(sourceViewer));
+		assistant.enableAutoActivation(true);
+		assistant.setAutoActivationDelay(500);
+
+		return assistant;
+	}
+
+	@Override
+	public IPresentationReconciler getPresentationReconciler(
+			final ISourceViewer sourceViewer) {
+		if (presentationReconciler == null) {
+			presentationReconciler = new EventBPresentationReconciler();
+			presentationReconciler
+					.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
+		}
+
+		return presentationReconciler;
+	}
+
+	@Override
+	public final IReconciler getReconciler(final ISourceViewer sourceViewer) {
+		final ReconcilingStrategy reconcileStrategy = new ReconcilingStrategy(
+				editor);
+
+		final MonoReconciler reconciler = new MonoReconciler(reconcileStrategy,
+				false);
+		reconciler.setIsAllowedToModifyDocument(true);
+		reconciler.setProgressMonitor(new NullProgressMonitor());
+		reconciler.setDelay(500);
+
+		return reconciler;
+	}
+
+	@Override
+	public String[] getConfiguredContentTypes(final ISourceViewer sourceViewer) {
+		return PartitionScanner.CONTENT_TYPES;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/TrimLinesAction.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/TrimLinesAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f9e0e55121e9196a1cac3e756a55326fd1b7276
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/TrimLinesAction.java
@@ -0,0 +1,185 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor;
+
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.TextEditorAction;
+
+public class TrimLinesAction extends TextEditorAction {
+
+	public TrimLinesAction(final ResourceBundle bundle, final String prefix,
+			final ITextEditor editor) {
+		super(bundle, prefix, editor);
+		update();
+	}
+
+	@Override
+	public void run() {
+		final ITextEditor editor = getTextEditor();
+		if (editor == null) {
+			return;
+		}
+
+		if (!validateEditorInputState()) {
+			return;
+		}
+
+		final IDocument document = getDocument(editor);
+		if (document == null) {
+			return;
+		}
+
+		int startLine = 0;
+		int endLine = document.getNumberOfLines() - 1;
+
+		final ITextSelection selection = getSelection(editor);
+		if (selection != null) {
+			startLine = selection.getStartLine();
+			endLine = selection.getEndLine();
+		}
+
+		try {
+			trimLines(document, startLine, endLine, SubMonitor.convert(null,
+					"Trimming lines", 1));
+
+			if (selection != null) {
+				final int startOffset = document.getLineOffset(selection
+						.getStartLine());
+				editor.selectAndReveal(startOffset, 0);
+			}
+		} catch (final BadLocationException e) {
+			// should not happen
+		}
+	}
+
+	/**
+	 * Returns the editor's document.
+	 * 
+	 * @param editor
+	 *            the editor
+	 * @return the editor's document
+	 */
+	private static IDocument getDocument(final ITextEditor editor) {
+		final IDocumentProvider documentProvider = editor.getDocumentProvider();
+		if (documentProvider == null) {
+			return null;
+		}
+
+		final IDocument document = documentProvider.getDocument(editor
+				.getEditorInput());
+		if (document == null) {
+			return null;
+		}
+
+		return document;
+	}
+
+	/**
+	 * Returns the editor's selection.
+	 * 
+	 * @param editor
+	 *            the editor
+	 * @return the editor's selection
+	 */
+	private static ITextSelection getSelection(final ITextEditor editor) {
+		final ISelectionProvider selectionProvider = editor
+				.getSelectionProvider();
+		if (selectionProvider == null) {
+			return null;
+		}
+
+		final ISelection selection = selectionProvider.getSelection();
+		if (!(selection instanceof ITextSelection)) {
+			return null;
+		}
+
+		return (ITextSelection) selection;
+	}
+
+	@Override
+	public void update() {
+		super.update();
+		if (!isEnabled()) {
+			return;
+		}
+
+		if (!canModifyEditor()) {
+			setEnabled(false);
+			return;
+		}
+
+		final ITextEditor editor = getTextEditor();
+		setEnabled(editor.isEditable());
+	}
+
+	public static int trimLines(final IDocument document, final int startLine,
+			final int endLine, final IProgressMonitor monitor)
+			throws BadLocationException {
+		final SubMonitor subMonitor = SubMonitor.convert(monitor);
+
+		if (startLine >= document.getNumberOfLines() || startLine > endLine) {
+			return 0;
+		}
+
+		int removeChars = 0;
+		final StringBuffer buffer = new StringBuffer();
+		subMonitor.setWorkRemaining(endLine - startLine + 1);
+
+		for (int line = startLine; line <= endLine; line++) {
+			final String trimmedLine = trim(document, line);
+			buffer.append(trimmedLine);
+			removeChars += document.getLineLength(line) - trimmedLine.length();
+
+			subMonitor.worked(1);
+		}
+
+		final int startLineOffset = document.getLineOffset(startLine);
+		final int endLineOffset = document.getLineOffset(endLine)
+				+ document.getLineLength(endLine);
+		final String replaceString = buffer.toString();
+
+		document.replace(startLineOffset, endLineOffset - startLineOffset,
+				replaceString);
+		subMonitor.worked(1);
+
+		return removeChars;
+	}
+
+	private static String trim(final IDocument document, final int line)
+			throws BadLocationException {
+		final int lineOffset = document.getLineOffset(line);
+		final int lineDelimiterLength = getLineDelimiterLength(document, line);
+		int lineLength = document.getLineLength(line) - lineDelimiterLength;
+
+		// correct new line length until no trailing whitespace left
+		while (lineLength > 0
+				&& Character.isWhitespace(document.getChar(lineOffset
+						+ lineLength - 1))) {
+			lineLength--;
+		}
+
+		final String lineDelimiter = lineDelimiterLength > 0 ? document
+				.getLineDelimiter(line) : "";
+		return document.get(lineOffset, lineLength) + lineDelimiter;
+	}
+
+	private static int getLineDelimiterLength(final IDocument document,
+			final int line) throws BadLocationException {
+		final String lineDelimiter = document.getLineDelimiter(line);
+		return lineDelimiter != null ? lineDelimiter.length() : 0;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/actions/FormatHandler.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/actions/FormatHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc5f4a15f509f35671281f91c4c3f2e3cd377150
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/actions/FormatHandler.java
@@ -0,0 +1,92 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor.actions;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.build.dom.DomManager.ParseResult;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+import org.eventb.texttools.prettyprint.PrettyPrinter;
+
+public class FormatHandler extends AbstractHandler {
+	public FormatHandler() {
+		super();
+		setBaseEnabled(true);
+	}
+
+	public Object execute(final ExecutionEvent event) throws ExecutionException {
+		final IEditorPart activeEditor = HandlerUtil.getActiveEditor(event);
+
+		if (activeEditor != null && activeEditor instanceof EventBTextEditor) {
+			try {
+				formatContent((EventBTextEditor) activeEditor);
+			} catch (final CoreException e) {
+				throw new ExecutionException("Content formatting failed", e);
+			}
+		}
+
+		return null;
+	}
+
+	private void formatContent(final EventBTextEditor editor)
+			throws CoreException {
+		if (!hasSyntaxErrors(editor)) {
+			final int cursorOffset = editor.getCursorOffset();
+
+			final IEditorInput editorInput = editor.getEditorInput();
+			final ParseResult parseResult = TextEditorPlugin.getDomManager()
+					.getLastParseResult(editorInput);
+
+			if (parseResult != null) {
+				final IDocument document = editor.getDocumentProvider()
+						.getDocument(editor.getEditorInput());
+
+				if (document != null) {
+					final String remberedContent = document.get();
+
+					final StringBuilder buffer = new StringBuilder();
+					new PrettyPrinter(buffer,
+							document.getLegalLineDelimiters()[0], null)
+							.prettyPrint(parseResult.astRoot);
+
+					final String newContent = buffer.toString();
+
+					if (!newContent.equals(remberedContent)) {
+						document.set(newContent);
+						editor.setCurserOffset(cursorOffset);
+					}
+				}
+			}
+		}
+	}
+
+	private boolean hasSyntaxErrors(final EventBTextEditor editor)
+			throws CoreException {
+		final IEditorInput editorInput = editor.getEditorInput();
+
+		if (editorInput instanceof IFileEditorInput) {
+			final IFileEditorInput fileInput = (IFileEditorInput) editorInput;
+			final IMarker[] markers = fileInput.getFile().findMarkers(
+					TextEditorPlugin.SYNTAXERROR_MARKER_ID, true,
+					IResource.DEPTH_INFINITE);
+
+			return markers.length > 0;
+		}
+
+		return true;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/actions/InsertHandler.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/actions/InsertHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..7994eef7c4aa8f7a7b63a5e91b1a0299dd0e4529
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/actions/InsertHandler.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Systerel and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Systerel - initial API and implementation
+ *******************************************************************************/
+package org.eventb.texteditor.ui.editor.actions;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+/**
+ * Handles insertion command (id="org.eventb.ui.edit.insert").
+ * 
+ * @author "Nicolas Beauger"
+ *
+ */
+public class InsertHandler extends AbstractHandler {
+
+	public Object execute(ExecutionEvent event) throws ExecutionException {
+		final IEditorPart activeEditor = HandlerUtil.getActiveEditor(event);
+
+		if (!(activeEditor instanceof EventBTextEditor)) {
+			return "Unexpected editor";
+		}
+
+		final String insertText = event
+				.getParameter("org.eventb.ui.edit.insert.text");
+		if (insertText == null) {
+			return "Unable to retrieve the text to insert";
+		}
+
+		((EventBTextEditor) activeEditor).insert(insertText, true);
+		return null;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/DefaultContentAssist.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/DefaultContentAssist.java
new file mode 100644
index 0000000000000000000000000000000000000000..623d96f2b4b9e046e57e0d8e5fb28468c7297db5
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/DefaultContentAssist.java
@@ -0,0 +1,425 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor.codecompletion;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.templates.Template;
+import org.eclipse.jface.text.templates.TemplateCompletionProcessor;
+import org.eclipse.jface.text.templates.TemplateContext;
+import org.eclipse.jface.text.templates.TemplateContextType;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.editors.text.templates.ContributionTemplateStore;
+import org.eventb.texteditor.ui.Images;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.build.dom.DomManager;
+import org.eventb.texteditor.ui.build.dom.IComponentDom;
+import org.eventb.texteditor.ui.build.dom.IDom;
+import org.eventb.texteditor.ui.build.dom.MachineDom;
+import org.eventb.texteditor.ui.build.dom.IDom.IdentifierType;
+import org.eventb.texteditor.ui.build.dom.IDom.Type;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+import org.eventb.texttools.Constants;
+
+/**
+ * Computes the content assist (code completion) proposals, i.e., for
+ * identifiers, keywords and templates.
+ * 
+ * It uses the following scheme to measure the proposals' relevance:
+ * <ul>
+ * <li>Identifiers get 100 points (to be always at the top of the list)</li>
+ * <li>Templates can reach 90 points</li>
+ * <ul>
+ * <li><code>Anywhere</code> templates get 90 points</li>
+ * <li>Others get 50 points for the correct compontent</li>
+ * <li><code>Events</code> templates can get 40 more points if the current
+ * offset is within the events section.</li>
+ * <li>All other get these 40 points per default</li>
+ * </ul>
+ * <li>Keywords can reach 80 points</li>
+ * <ul>
+ * <li>All templates get 50 points for the correct compontent</li>
+ * <li>The remaining 30 points are given depending on the template type and the
+ * current offset</li>
+ * </ul>
+ * </ul>
+ * 
+ */
+public class DefaultContentAssist extends TemplateCompletionProcessor {
+
+	public enum ContextType {
+		Unkown(PREFIX + "unknown"), Anywhere(PREFIX + "anywhere"), Machine(
+				PREFIX + "machine"), Context(PREFIX + "context"), Events(PREFIX
+				+ "events");
+
+		public final String key;
+
+		ContextType(final String key) {
+			this.key = key;
+		}
+	};
+
+	private static final ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0];
+	private static final String PREFIX = "org.eventb.texteditor.";
+
+	private ContributionTemplateStore templateStore;
+	private final DomManager domManager = TextEditorPlugin.getDomManager();
+	private final EventBTextEditor editor;
+
+	private String errorMessage;
+	private String contentType;
+	private Region region;
+	private String prefix;
+	private IComponentDom dom;
+	private IDom scopingDom;
+	private Type componentType;
+	private IDocument document;
+	private ITextViewer viewer;
+	private boolean offsetInEvents;
+
+	public DefaultContentAssist(final EventBTextEditor editor) {
+		this.editor = editor;
+	}
+
+	@Override
+	public ICompletionProposal[] computeCompletionProposals(
+			final ITextViewer viewer, final int offset) {
+		errorMessage = null;
+
+		try {
+			setupContextInfo(viewer, offset);
+
+			final List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
+			computeIdentifierProposals(proposals);
+			computeKeywordProposals(proposals);
+			computeTemplateProposals(proposals);
+
+			return sort(proposals);
+		} catch (final BadLocationException e) {
+			errorMessage = "Error while calculating content type";
+		}
+
+		return NO_PROPOSALS;
+	}
+
+	@Override
+	protected ICompletionProposal createProposal(final Template template,
+			final TemplateContext context, final IRegion region,
+			final int relevance) {
+		return new EventBTemplateProposal(template, context, region,
+				getImage(template), relevance);
+	}
+
+	private void setupContextInfo(final ITextViewer viewer, int offset)
+			throws BadLocationException {
+		this.viewer = viewer;
+		offset = adjustOffset(viewer, offset);
+
+		document = viewer.getDocument();
+		contentType = document.getContentType(offset);
+		prefix = extractPrefix(viewer, offset);
+		region = new Region(offset - prefix.length(), prefix.length());
+
+		final int eventsOffset = document.get().indexOf(Constants.EVENTS);
+		offsetInEvents = offset >= eventsOffset;
+
+		dom = domManager.getDom(editor.getResource());
+
+		if (dom != null) {
+			scopingDom = dom.getScopingDom(offset);
+			componentType = dom instanceof MachineDom ? Type.Machine
+					: Type.Context;
+		}
+	}
+
+	private ICompletionProposal[] sort(final List<ICompletionProposal> proposals) {
+		// sort by relevance and lexically
+		Collections.sort(proposals, new ProposalComparator());
+		return proposals.toArray(new ICompletionProposal[proposals.size()]);
+	}
+
+	private void computeTemplateProposals(
+			final List<ICompletionProposal> proposals) {
+		final Template[] templates = getTemplates();
+		final TemplateContext context = createContext(viewer, region);
+
+		for (final Template template : templates) {
+			if (template.getName().startsWith(prefix)) {
+				final EventBTemplateProposal proposal = new EventBTemplateProposal(
+						template, context, region, getImage(template),
+						getRelevance(template, prefix));
+
+				proposals.add(proposal);
+			}
+		}
+	}
+
+	private void computeKeywordProposals(
+			final List<ICompletionProposal> proposals) {
+		if (!IDocument.DEFAULT_CONTENT_TYPE.equals(contentType)) {
+			return;
+		}
+
+		final Map<String, ICompletionProposal> result = new HashMap<String, ICompletionProposal>();
+
+		int componentRelevance = 50;
+		int scopeRelevance = 30;
+		addKeywords(result, Arrays.asList(Constants.formula_keywords),
+				componentRelevance, scopeRelevance);
+
+		componentRelevance = componentType == Type.Machine ? 50 : 0;
+		scopeRelevance = offsetInEvents ? 30 : 0;
+		addKeywords(result, Arrays.asList(Constants.event_keywords),
+				componentRelevance, scopeRelevance);
+
+		componentRelevance = componentType == Type.Machine ? 50 : 0;
+		scopeRelevance = offsetInEvents ? 20 : 30;
+		addKeywords(result, Arrays.asList(Constants.machine_keywords),
+				componentRelevance, scopeRelevance);
+
+		componentRelevance = componentType == Type.Context ? 50 : 0;
+		scopeRelevance = offsetInEvents ? 0 : 30;
+		addKeywords(result, Arrays.asList(Constants.context_keywords),
+				componentRelevance, scopeRelevance);
+
+		proposals.addAll(result.values());
+	}
+
+	private void addKeywords(final Map<String, ICompletionProposal> result,
+			final List<String> keywords, final int componentRelevance,
+			final int scopeRelevance) {
+		for (final String keyword : keywords) {
+			if (keyword.startsWith(prefix)) {
+				final ICompletionProposal oldProposal = result.get(keyword);
+				final int relevance = componentRelevance + scopeRelevance;
+
+				// adjust relevance if already present
+				if (oldProposal != null) {
+					if (oldProposal instanceof IRelevantProposal) {
+						final EventBCompletionProposal evtBProp = (EventBCompletionProposal) oldProposal;
+						final int oldRelevance = evtBProp.getRelevance();
+
+						if (oldRelevance < relevance) {
+							evtBProp.changeRelevance(relevance - oldRelevance);
+						}
+					}
+				} else {
+					final EventBCompletionProposal proposal = new EventBCompletionProposal(
+							keyword, region.getOffset(), prefix.length(),
+							keyword.length(), null, keyword, null, "keyword '"
+									+ keyword + "'");
+					proposal.changeRelevance(relevance);
+
+					result.put(keyword, proposal);
+				}
+			}
+		}
+	}
+
+	private void computeIdentifierProposals(
+			final List<ICompletionProposal> proposals) {
+		if (dom == null || scopingDom == null
+				|| !IDocument.DEFAULT_CONTENT_TYPE.equals(contentType)) {
+			return;
+		}
+
+		final Set<String> identifiers = scopingDom.getIdentifiers();
+		for (final String ident : identifiers) {
+			if (ident.startsWith(prefix)) {
+				final IdentifierType type = scopingDom.getIdentifierType(ident);
+
+				final String description = createDescription(ident, type);
+				final Image image = getImage(type);
+
+				final EventBCompletionProposal proposal = new EventBCompletionProposal(
+						ident, region.getOffset(), prefix.length(), ident
+								.length(), image, ident, null, description);
+				proposals.add(proposal);
+				proposal.changeRelevance(100);
+			}
+		}
+	}
+
+	private String createDescription(final String ident,
+			final IdentifierType type) {
+		final StringBuffer description = new StringBuffer(ident);
+
+		if (type != null) {
+			switch (type) {
+			case GlobalVariable:
+				description.append(" : global variable");
+				break;
+			case LocalVariable:
+				description.append(" : local variable");
+				break;
+			case Parameter:
+				description.append(" : event parameter");
+				break;
+			case Constant:
+				description.append(" : constant");
+				break;
+			case Set:
+				description.append(" : carrier set");
+				break;
+			default:
+				return null;
+			}
+		}
+
+		return description.toString();
+	}
+
+	private Image getImage(final IdentifierType type) {
+		if (type != null) {
+			switch (type) {
+			case GlobalVariable:
+			case LocalVariable:
+			case Parameter:
+				return Images.getImage(Images.IMG_VARIABLE);
+			case Constant:
+				return Images.getImage(Images.IMG_CONSTANT);
+			case Set:
+				return Images.getImage(Images.IMG_CARRIER_SET);
+			}
+		}
+
+		return null;
+	}
+
+	private int adjustOffset(final ITextViewer viewer, int offset) {
+		// adjust offset to end of normalized selection
+		final ITextSelection selection = (ITextSelection) viewer
+				.getSelectionProvider().getSelection();
+		if (selection.getOffset() == offset) {
+			offset = selection.getOffset() + selection.getLength();
+		}
+		return offset;
+	}
+
+	@Override
+	protected TemplateContextType getContextType(final ITextViewer viewer,
+			final IRegion region) {
+		// we don't care about context types here
+		return new TemplateContextType(ContextType.Unkown.key);
+	}
+
+	@Override
+	protected Image getImage(final Template template) {
+		return Images.getImage(Images.IMG_TEMPLATE, Images.IMG_TEMPLATE_PATH);
+	}
+
+	private Template[] getTemplates() {
+		if (templateStore == null) {
+			templateStore = TextEditorPlugin.getPlugin().getTemplateStore();
+		}
+
+		/*
+		 * We don't filter by context types here. They are consider when
+		 * calculating the relevance.
+		 */
+		return templateStore.getTemplates(null);
+	}
+
+	@Override
+	protected Template[] getTemplates(final String contextTypeId) {
+		return getTemplates();
+	}
+
+	@Override
+	protected int getRelevance(final Template template, final String prefix) {
+		final String id = template.getContextTypeId();
+
+		// These templates are relevant everywhere
+		if (ContextType.Anywhere.key.equals(id)) {
+			return 90;
+		}
+
+		int relevance = 0;
+
+		if (componentType != null) {
+			// 50 points for matching component
+			switch (componentType) {
+			case Machine:
+				if (ContextType.Machine.key.equals(id)
+						|| ContextType.Events.key.equals(id)) {
+					relevance += 50;
+				}
+				break;
+			case Context:
+				if (ContextType.Context.key.equals(id)) {
+					relevance += 50;
+				}
+				break;
+			}
+		} else {
+			// fallback: do not distinguish
+			relevance += 50;
+		}
+
+		/*
+		 * Templates for the events section get their points when the current
+		 * offset is in this section.
+		 */
+		if (ContextType.Events.key.equals(id)) {
+			if (offsetInEvents) {
+				relevance += 40;
+			}
+		}
+		// other templates get these points in all cases
+		else {
+			relevance += 40;
+		}
+
+		return relevance;
+	}
+
+	@Override
+	public String getErrorMessage() {
+		return errorMessage;
+	}
+
+	private final class ProposalComparator implements
+			Comparator<ICompletionProposal> {
+		public int compare(final ICompletionProposal o1,
+				final ICompletionProposal o2) {
+			final int rel1 = getRelevance(o1);
+			final int rel2 = getRelevance(o2);
+			final int diff = rel2 - rel1;
+
+			if (diff != 0 && rel1 >= 0 && rel2 >= 0) {
+				return diff;
+			}
+
+			// fall back to alpha-numerical comparison
+			return o1.getDisplayString().toLowerCase().compareTo(
+					o2.getDisplayString().toLowerCase());
+		}
+
+		private int getRelevance(final ICompletionProposal o) {
+
+			if (o instanceof IRelevantProposal) {
+				return ((IRelevantProposal) o).getRelevance();
+			}
+
+			return -1;
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EditorSynchronizer.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EditorSynchronizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..6826211ebb38753c63e124ae35fcd6e7a1866041
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EditorSynchronizer.java
@@ -0,0 +1,33 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor.codecompletion;
+
+import org.eclipse.jface.text.link.ILinkedModeListener;
+import org.eclipse.jface.text.link.LinkedModeModel;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+public class EditorSynchronizer implements ILinkedModeListener {
+
+	private final EventBTextEditor editor;
+
+	public EditorSynchronizer(final EventBTextEditor editor) {
+		this.editor = editor;
+		editor.setInLinkedMode(true);
+	}
+
+	public void left(final LinkedModeModel model, final int flags) {
+		editor.setInLinkedMode(false);
+	}
+
+	public void resume(final LinkedModeModel model, final int flags) {
+		// IGNORE
+	}
+
+	public void suspend(final LinkedModeModel model) {
+		// IGNORE
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EventBCompletionProposal.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EventBCompletionProposal.java
new file mode 100644
index 0000000000000000000000000000000000000000..b42b93ec5a30f7c5d6efb6cec2257443d7ba3e95
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EventBCompletionProposal.java
@@ -0,0 +1,161 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor.codecompletion;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+public class EventBCompletionProposal implements IRelevantProposal,
+		ICompletionProposal {
+
+	/** The string to be displayed in the completion proposal popup. */
+	private final String fDisplayString;
+	/** The replacement string. */
+	private final String fReplacementString;
+	/** The replacement offset. */
+	private final int fReplacementOffset;
+	/** The replacement length. */
+	private final int fReplacementLength;
+	/** The cursor position after this proposal has been applied. */
+	private final int fCursorPosition;
+	/** The image to be displayed in the completion proposal popup. */
+	private final Image fImage;
+	/** The context information of this proposal. */
+	private final IContextInformation fContextInformation;
+	/** The additional info of this proposal. */
+	private final String fAdditionalProposalInfo;
+
+	private int relevance;
+
+	/**
+	 * Creates a new completion proposal based on the provided information. The
+	 * replacement string is considered being the display string too. All
+	 * remaining fields are set to <code>null</code>.
+	 * 
+	 * @param replacementString
+	 *            the actual string to be inserted into the document
+	 * @param replacementOffset
+	 *            the offset of the text to be replaced
+	 * @param replacementLength
+	 *            the length of the text to be replaced
+	 * @param cursorPosition
+	 *            the position of the cursor following the insert relative to
+	 *            replacementOffset
+	 */
+	public EventBCompletionProposal(final String replacementString,
+			final int replacementOffset, final int replacementLength,
+			final int cursorPosition) {
+		this(replacementString, replacementOffset, replacementLength,
+				cursorPosition, null, null, null, null);
+	}
+
+	/**
+	 * Creates a new completion proposal. All fields are initialized based on
+	 * the provided information.
+	 * 
+	 * @param replacementString
+	 *            the actual string to be inserted into the document
+	 * @param replacementOffset
+	 *            the offset of the text to be replaced
+	 * @param replacementLength
+	 *            the length of the text to be replaced
+	 * @param cursorPosition
+	 *            the position of the cursor following the insert relative to
+	 *            replacementOffset
+	 * @param image
+	 *            the image to display for this proposal
+	 * @param displayString
+	 *            the string to be displayed for the proposal
+	 * @param contextInformation
+	 *            the context information associated with this proposal
+	 * @param additionalProposalInfo
+	 *            the additional information associated with this proposal
+	 */
+	public EventBCompletionProposal(final String replacementString,
+			final int replacementOffset, final int replacementLength,
+			final int cursorPosition, final Image image,
+			final String displayString,
+			final IContextInformation contextInformation,
+			final String additionalProposalInfo) {
+		Assert.isNotNull(replacementString);
+		Assert.isTrue(replacementOffset >= 0);
+		Assert.isTrue(replacementLength >= 0);
+		Assert.isTrue(cursorPosition >= 0);
+
+		fReplacementString = replacementString;
+		fReplacementOffset = replacementOffset;
+		fReplacementLength = replacementLength;
+		fCursorPosition = cursorPosition;
+		fImage = image;
+		fDisplayString = displayString;
+		fContextInformation = contextInformation;
+		fAdditionalProposalInfo = additionalProposalInfo;
+	}
+
+	/*
+	 * @see ICompletionProposal#apply(IDocument)
+	 */
+	public void apply(final IDocument document) {
+		try {
+			document.replace(fReplacementOffset, fReplacementLength,
+					fReplacementString);
+		} catch (final BadLocationException x) {
+			// ignore
+		}
+	}
+
+	/*
+	 * @see ICompletionProposal#getSelection(IDocument)
+	 */
+	public Point getSelection(final IDocument document) {
+		return new Point(fReplacementOffset + fCursorPosition, 0);
+	}
+
+	/*
+	 * @see ICompletionProposal#getContextInformation()
+	 */
+	public IContextInformation getContextInformation() {
+		return fContextInformation;
+	}
+
+	/*
+	 * @see ICompletionProposal#getImage()
+	 */
+	public Image getImage() {
+		return fImage;
+	}
+
+	/*
+	 * @see ICompletionProposal#getDisplayString()
+	 */
+	public String getDisplayString() {
+		if (fDisplayString != null) {
+			return fDisplayString;
+		}
+		return fReplacementString;
+	}
+
+	/*
+	 * @see ICompletionProposal#getAdditionalProposalInfo()
+	 */
+	public String getAdditionalProposalInfo() {
+		return fAdditionalProposalInfo;
+	}
+
+	public int getRelevance() {
+		return relevance;
+	}
+
+	public void changeRelevance(final int byValue) {
+		relevance += byValue;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EventBTemplateProposal.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EventBTemplateProposal.java
new file mode 100644
index 0000000000000000000000000000000000000000..01d19604db6571bb832b567820ff4e9dbda83b74
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/EventBTemplateProposal.java
@@ -0,0 +1,264 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor.codecompletion;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
+import org.eclipse.jface.text.link.ILinkedModeListener;
+import org.eclipse.jface.text.link.InclusivePositionUpdater;
+import org.eclipse.jface.text.link.LinkedModeModel;
+import org.eclipse.jface.text.link.LinkedModeUI;
+import org.eclipse.jface.text.link.LinkedPosition;
+import org.eclipse.jface.text.link.LinkedPositionGroup;
+import org.eclipse.jface.text.link.ProposalPosition;
+import org.eclipse.jface.text.templates.GlobalTemplateVariables;
+import org.eclipse.jface.text.templates.Template;
+import org.eclipse.jface.text.templates.TemplateBuffer;
+import org.eclipse.jface.text.templates.TemplateContext;
+import org.eclipse.jface.text.templates.TemplateException;
+import org.eclipse.jface.text.templates.TemplateProposal;
+import org.eclipse.jface.text.templates.TemplateVariable;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+public class EventBTemplateProposal extends TemplateProposal implements
+		IRelevantProposal, ICompletionProposal, ICompletionProposalExtension,
+		ICompletionProposalExtension2, ICompletionProposalExtension3 {
+
+	private final IRegion fRegion;
+
+	private IRegion fSelectedRegion; // initialized by apply()
+	private InclusivePositionUpdater fUpdater;
+
+	public EventBTemplateProposal(final Template template,
+			final TemplateContext context, final IRegion region,
+			final Image image, final int relevance) {
+		super(template, context, region, image, relevance);
+
+		fRegion = region;
+	}
+
+	/**
+	 * Inserts the template offered by this proposal into the viewer's document
+	 * and sets up a <code>LinkedModeUI</code> on the viewer to edit any of the
+	 * template's unresolved variables.
+	 * 
+	 * @param viewer
+	 *            {@inheritDoc}
+	 * @param trigger
+	 *            {@inheritDoc}
+	 * @param stateMask
+	 *            {@inheritDoc}
+	 * @param offset
+	 *            {@inheritDoc}
+	 */
+	@Override
+	public void apply(final ITextViewer viewer, final char trigger,
+			final int stateMask, final int offset) {
+
+		final IDocument document = viewer.getDocument();
+		try {
+			final TemplateContext fContext = getContext();
+			fContext.setReadOnly(false);
+			int start;
+			TemplateBuffer templateBuffer;
+			{
+				final int oldReplaceOffset = getReplaceOffset();
+				try {
+					// this may already modify the document (e.g. add imports)
+					templateBuffer = fContext.evaluate(getTemplate());
+				} catch (final TemplateException e1) {
+					fSelectedRegion = fRegion;
+					return;
+				}
+
+				start = getReplaceOffset();
+				final int shift = start - oldReplaceOffset;
+				final int end = Math.max(getReplaceEndOffset(), offset + shift);
+
+				// insert template string
+				final String templateString = templateBuffer.getString();
+				document.replace(start, end - start, templateString);
+			}
+
+			// translate positions
+			final LinkedModeModel model = new LinkedModeModel();
+			final TemplateVariable[] variables = templateBuffer.getVariables();
+			boolean hasPositions = false;
+			for (int i = 0; i != variables.length; i++) {
+				final TemplateVariable variable = variables[i];
+
+				if (variable.isUnambiguous()) {
+					continue;
+				}
+
+				final LinkedPositionGroup group = new LinkedPositionGroup();
+
+				final int[] offsets = variable.getOffsets();
+				final int length = variable.getLength();
+
+				LinkedPosition first;
+				{
+					final String[] values = variable.getValues();
+					final ICompletionProposal[] proposals = new ICompletionProposal[values.length];
+					for (int j = 0; j < values.length; j++) {
+						ensurePositionCategoryInstalled(document, model);
+						final Position pos = new Position(offsets[0] + start,
+								length);
+						document.addPosition(getCategory(), pos);
+						proposals[j] = new PositionBasedCompletionProposal(
+								values[j], pos, length);
+					}
+
+					if (proposals.length > 1) {
+						first = new ProposalPosition(document, offsets[0]
+								+ start, length, proposals);
+					} else {
+						first = new LinkedPosition(document,
+								offsets[0] + start, length);
+					}
+				}
+
+				for (int j = 0; j != offsets.length; j++) {
+					if (j == 0) {
+						group.addPosition(first);
+					} else {
+						group.addPosition(new LinkedPosition(document,
+								offsets[j] + start, length));
+					}
+				}
+
+				model.addGroup(group);
+				hasPositions = true;
+			}
+
+			if (hasPositions) {
+				model.forceInstall();
+
+				final EventBTextEditor editor = getEventBEditor();
+				if (editor != null) {
+					model.addLinkingListener(new EditorSynchronizer(editor));
+				}
+
+				final LinkedModeUI ui = new EditorLinkedModeUI(model, viewer);
+				ui.setExitPosition(viewer, getCaretOffset(templateBuffer)
+						+ start, 0, Integer.MAX_VALUE);
+				ui.enter();
+
+				fSelectedRegion = ui.getSelectedRegion();
+			} else {
+				ensurePositionCategoryRemoved(document);
+				fSelectedRegion = new Region(getCaretOffset(templateBuffer)
+						+ start, 0);
+			}
+		} catch (final BadLocationException e) {
+			openErrorDialog(viewer.getTextWidget().getShell(), e);
+			ensurePositionCategoryRemoved(document);
+			fSelectedRegion = fRegion;
+		} catch (final BadPositionCategoryException e) {
+			openErrorDialog(viewer.getTextWidget().getShell(), e);
+			fSelectedRegion = fRegion;
+		}
+
+	}
+
+	private EventBTextEditor getEventBEditor() {
+		final IEditorPart part = TextEditorPlugin.getPlugin().getActivePage()
+				.getActiveEditor();
+		if (part instanceof EventBTextEditor) {
+			return (EventBTextEditor) part;
+		} else {
+			return null;
+		}
+	}
+
+	private void ensurePositionCategoryInstalled(final IDocument document,
+			final LinkedModeModel model) {
+		if (!document.containsPositionCategory(getCategory())) {
+			document.addPositionCategory(getCategory());
+			fUpdater = new InclusivePositionUpdater(getCategory());
+			document.addPositionUpdater(fUpdater);
+
+			model.addLinkingListener(new ILinkedModeListener() {
+
+				/*
+				 * @see
+				 * org.eclipse.jface.text.link.ILinkedModeListener#left(org.
+				 * eclipse.jface.text.link.LinkedModeModel, int)
+				 */
+				public void left(final LinkedModeModel environment,
+						final int flags) {
+					ensurePositionCategoryRemoved(document);
+				}
+
+				public void suspend(final LinkedModeModel environment) {
+				}
+
+				public void resume(final LinkedModeModel environment,
+						final int flags) {
+				}
+			});
+		}
+	}
+
+	private void ensurePositionCategoryRemoved(final IDocument document) {
+		if (document.containsPositionCategory(getCategory())) {
+			try {
+				document.removePositionCategory(getCategory());
+			} catch (final BadPositionCategoryException e) {
+				// ignore
+			}
+			document.removePositionUpdater(fUpdater);
+		}
+	}
+
+	private String getCategory() {
+		return "TemplateProposalCategory_" + toString(); //$NON-NLS-1$
+	}
+
+	private int getCaretOffset(final TemplateBuffer buffer) {
+
+		final TemplateVariable[] variables = buffer.getVariables();
+		for (int i = 0; i != variables.length; i++) {
+			final TemplateVariable variable = variables[i];
+			if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME)) {
+				return variable.getOffsets()[0];
+			}
+		}
+
+		return buffer.getString().length();
+	}
+
+	private void openErrorDialog(final Shell shell, final Exception e) {
+		MessageDialog.openError(shell, "Template Evaluation Error", e
+				.getMessage());
+	}
+
+	/*
+	 * @see ICompletionProposal#getSelection(IDocument)
+	 */
+	@Override
+	public Point getSelection(final IDocument document) {
+		return new Point(fSelectedRegion.getOffset(), fSelectedRegion
+				.getLength());
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/IRelevantProposal.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/IRelevantProposal.java
new file mode 100644
index 0000000000000000000000000000000000000000..69ce7f32998292cf4229b66eaa53f490645e5f69
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/IRelevantProposal.java
@@ -0,0 +1,13 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor.codecompletion;
+
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+
+public interface IRelevantProposal extends ICompletionProposal {
+	public int getRelevance();
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/PositionBasedCompletionProposal.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/PositionBasedCompletionProposal.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce484f68a03264fef842ff1be21218678d5a2daa
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/editor/codecompletion/PositionBasedCompletionProposal.java
@@ -0,0 +1,198 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.editor.codecompletion;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+/**
+ * A position based completion proposal.
+ * 
+ * @since 3.0
+ */
+final class PositionBasedCompletionProposal implements ICompletionProposal,
+		ICompletionProposalExtension2 {
+
+	/** The string to be displayed in the completion proposal popup */
+	private final String fDisplayString;
+	/** The replacement string */
+	private final String fReplacementString;
+	/** The replacement position. */
+	private final Position fReplacementPosition;
+	/** The cursor position after this proposal has been applied */
+	private final int fCursorPosition;
+	/** The image to be displayed in the completion proposal popup */
+	private final Image fImage;
+	/** The context information of this proposal */
+	private final IContextInformation fContextInformation;
+	/** The additional info of this proposal */
+	private final String fAdditionalProposalInfo;
+
+	/**
+	 * Creates a new completion proposal based on the provided information. The
+	 * replacement string is considered being the display string too. All
+	 * remaining fields are set to <code>null</code>.
+	 * 
+	 * @param replacementString
+	 *            the actual string to be inserted into the document
+	 * @param replacementPosition
+	 *            the position of the text to be replaced
+	 * @param cursorPosition
+	 *            the position of the cursor following the insert relative to
+	 *            replacementOffset
+	 */
+	public PositionBasedCompletionProposal(final String replacementString,
+			final Position replacementPosition, final int cursorPosition) {
+		this(replacementString, replacementPosition, cursorPosition, null,
+				null, null, null);
+	}
+
+	/**
+	 * Creates a new completion proposal. All fields are initialized based on
+	 * the provided information.
+	 * 
+	 * @param replacementString
+	 *            the actual string to be inserted into the document
+	 * @param replacementPosition
+	 *            the position of the text to be replaced
+	 * @param cursorPosition
+	 *            the position of the cursor following the insert relative to
+	 *            replacementOffset
+	 * @param image
+	 *            the image to display for this proposal
+	 * @param displayString
+	 *            the string to be displayed for the proposal
+	 * @param contextInformation
+	 *            the context information associated with this proposal
+	 * @param additionalProposalInfo
+	 *            the additional information associated with this proposal
+	 */
+	public PositionBasedCompletionProposal(final String replacementString,
+			final Position replacementPosition, final int cursorPosition,
+			final Image image, final String displayString,
+			final IContextInformation contextInformation,
+			final String additionalProposalInfo) {
+		Assert.isNotNull(replacementString);
+		Assert.isTrue(replacementPosition != null);
+
+		fReplacementString = replacementString;
+		fReplacementPosition = replacementPosition;
+		fCursorPosition = cursorPosition;
+		fImage = image;
+		fDisplayString = displayString;
+		fContextInformation = contextInformation;
+		fAdditionalProposalInfo = additionalProposalInfo;
+	}
+
+	/*
+	 * @see ICompletionProposal#apply(IDocument)
+	 */
+	public void apply(final IDocument document) {
+		try {
+			document.replace(fReplacementPosition.getOffset(),
+					fReplacementPosition.getLength(), fReplacementString);
+		} catch (final BadLocationException x) {
+			// ignore
+		}
+	}
+
+	/*
+	 * @see ICompletionProposal#getSelection(IDocument)
+	 */
+	public Point getSelection(final IDocument document) {
+		return new Point(fReplacementPosition.getOffset() + fCursorPosition, 0);
+	}
+
+	/*
+	 * @see ICompletionProposal#getContextInformation()
+	 */
+	public IContextInformation getContextInformation() {
+		return fContextInformation;
+	}
+
+	/*
+	 * @see ICompletionProposal#getImage()
+	 */
+	public Image getImage() {
+		return fImage;
+	}
+
+	/*
+	 * @see
+	 * org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString
+	 * ()
+	 */
+	public String getDisplayString() {
+		if (fDisplayString != null) {
+			return fDisplayString;
+		}
+		return fReplacementString;
+	}
+
+	/*
+	 * @see ICompletionProposal#getAdditionalProposalInfo()
+	 */
+	public String getAdditionalProposalInfo() {
+		return fAdditionalProposalInfo;
+	}
+
+	/*
+	 * @see
+	 * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply
+	 * (org.eclipse.jface.text.ITextViewer, char, int, int)
+	 */
+	public void apply(final ITextViewer viewer, final char trigger,
+			final int stateMask, final int offset) {
+		apply(viewer.getDocument());
+	}
+
+	/*
+	 * @see
+	 * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected
+	 * (org.eclipse.jface.text.ITextViewer, boolean)
+	 */
+	public void selected(final ITextViewer viewer, final boolean smartToggle) {
+	}
+
+	/*
+	 * @see
+	 * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected
+	 * (org.eclipse.jface.text.ITextViewer)
+	 */
+	public void unselected(final ITextViewer viewer) {
+	}
+
+	/*
+	 * @see
+	 * org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate
+	 * (org.eclipse.jface.text.IDocument, int,
+	 * org.eclipse.jface.text.DocumentEvent)
+	 */
+	public boolean validate(final IDocument document, final int offset,
+			final DocumentEvent event) {
+		try {
+			final String content = document.get(fReplacementPosition
+					.getOffset(), offset - fReplacementPosition.getOffset());
+			if (fReplacementString.startsWith(content)) {
+				return true;
+			}
+		} catch (final BadLocationException e) {
+			// ignore concurrently modified document
+		}
+		return false;
+	}
+
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContentProvider.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContentProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..c62af5447617971245e1f0aef8268018c27a7dae
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContentProvider.java
@@ -0,0 +1,232 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IFileEditorInput;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.Variant;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.build.dom.DomManager;
+import org.eventb.texteditor.ui.build.dom.IParseResultListener;
+import org.eventb.texteditor.ui.build.dom.DomManager.ParseResult;
+
+/**
+ * {@link ITreeContentProvider} which can handle {@link IEditorInput}s as input.
+ */
+public class ContentProvider implements ITreeContentProvider,
+		IParseResultListener {
+
+	private final DomManager domManager = TextEditorPlugin.getDomManager();
+
+	private static final Object[] NO_ELEMENTS = new Object[] {};
+
+	/**
+	 * Cache for children of a machine
+	 */
+	private Object[] machineChildren;
+
+	/**
+	 * Cache for children of a context
+	 */
+	private Object[] contextChildren;
+
+	/**
+	 * Caches for children of events
+	 */
+	private final Map<Event, Object[]> eventChildren = new HashMap<Event, Object[]>();
+
+	private TreeViewer viewer;
+
+	private Object currentInput;
+
+	synchronized public Object[] getElements(final Object inputElement) {
+		if (inputElement instanceof IEditorInput) {
+			// show root element based on last successful parse result
+			final ParseResult lastParseResult = domManager
+					.getLastParseResult((IEditorInput) inputElement);
+
+			if (lastParseResult != null) {
+				return new Object[] { lastParseResult.astRoot };
+			} else if (inputElement instanceof IFileEditorInput) {
+				final IFile inputFile = ((IFileEditorInput) inputElement)
+						.getFile();
+				return createFallbackStructure(inputFile);
+			}
+		}
+
+		return NO_ELEMENTS;
+	}
+
+	synchronized public Object[] getChildren(final Object parentElement) {
+		if (parentElement instanceof Machine) {
+			return internalGetChildren((Machine) parentElement);
+		}
+
+		if (parentElement instanceof Context) {
+			return internalGetChildren((Context) parentElement);
+		}
+
+		if (parentElement instanceof Event) {
+			return internalGetChildren((Event) parentElement);
+		}
+
+		// no children for unknown parents
+		return NO_ELEMENTS;
+	}
+
+	synchronized public Object getParent(final Object element) {
+		// node is one of our emf classes
+		if (element instanceof EventBObject) {
+			final EventBObject emfObject = (EventBObject) element;
+			return emfObject.eContainer();
+		}
+
+		// we cannot compute the parent
+		return null;
+	}
+
+	synchronized public boolean hasChildren(final Object element) {
+		int count = 0;
+
+		if (element instanceof Machine) {
+			count = internalGetChildren((Machine) element).length;
+		}
+
+		if (element instanceof Context) {
+			count = internalGetChildren((Context) element).length;
+		}
+
+		if (element instanceof Event) {
+			count = internalGetChildren((Event) element).length;
+		}
+
+		return count > 0;
+	}
+
+	synchronized public void inputChanged(final Viewer viewer,
+			final Object oldInput, final Object newInput) {
+		registerLister(newInput);
+
+		if (viewer instanceof TreeViewer) {
+			this.viewer = (TreeViewer) viewer;
+		}
+
+		clearCache();
+		currentInput = newInput;
+	}
+
+	synchronized public void dispose() {
+		registerLister(null);
+		viewer = null;
+		clearCache();
+	}
+
+	private void clearCache() {
+		machineChildren = null;
+		contextChildren = null;
+		eventChildren.clear();
+	}
+
+	private void registerLister(final Object newInput) {
+		if (currentInput != null && currentInput instanceof IEditorInput) {
+			domManager.removeParseResultListener((IEditorInput) currentInput,
+					this);
+		}
+
+		if (newInput != null && newInput instanceof IEditorInput) {
+			domManager.addParseResultListener((IEditorInput) newInput, this);
+		}
+	}
+
+	private Object[] createFallbackStructure(final IFile inputFile) {
+		/*
+		 * TODO Use fallback strategy if no AST available, for example scan text
+		 * for some keywords and use them to offer quick navigation by clicking
+		 */
+		return NO_ELEMENTS;
+	}
+
+	private Object[] internalGetChildren(final Machine machine) {
+		if (machineChildren == null) {
+			final ArrayList<EventBObject> children = new ArrayList<EventBObject>();
+			children.addAll(machine.getVariables());
+			children.addAll(machine.getInvariants());
+
+			final Variant variant = machine.getVariant();
+			if (variant != null) {
+				children.add(variant);
+			}
+
+			children.addAll(machine.getEvents());
+
+			machineChildren = children.toArray();
+		}
+
+		return machineChildren;
+	}
+
+	private Object[] internalGetChildren(final Context context) {
+		if (contextChildren == null) {
+			final ArrayList<EventBObject> children = new ArrayList<EventBObject>();
+			children.addAll(context.getConstants());
+			children.addAll(context.getAxioms());
+			children.addAll(context.getSets());
+
+			contextChildren = children.toArray();
+		}
+
+		return contextChildren;
+	}
+
+	private Object[] internalGetChildren(final Event event) {
+		if (!eventChildren.containsKey(event)) {
+			final ArrayList<EventBObject> children = new ArrayList<EventBObject>();
+			children.addAll(event.getWitnesses());
+			children.addAll(event.getGuards());
+			children.addAll(event.getActions());
+
+			eventChildren.put(event, children.toArray());
+		}
+
+		return eventChildren.get(event);
+	}
+
+	private void refresh() {
+		if (viewer != null && viewer.getControl() != null
+				&& !viewer.getControl().isDisposed()) {
+			viewer.refresh();
+			viewer.expandAll();
+		}
+	}
+
+	public void parseResultChanged(final ParseResult parseResult) {
+		if (viewer != null && viewer.getControl() != null
+				&& !viewer.getControl().isDisposed()) {
+			// remove our now old cache so it can be rebuild
+			clearCache();
+
+			// refresh the viewer async because we might not be in the UI thread
+			viewer.getControl().getDisplay().asyncExec(new Runnable() {
+				public void run() {
+					refresh();
+				}
+			});
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextImageSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextImageSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..5673250188247b5467d6185ff9c783dc19b844c3
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextImageSwitch.java
@@ -0,0 +1,45 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eclipse.swt.graphics.Image;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.Axiom;
+import org.eventb.emf.core.context.CarrierSet;
+import org.eventb.emf.core.context.Constant;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.context.util.ContextSwitch;
+import org.eventb.texteditor.ui.Images;
+
+public class ContextImageSwitch extends ContextSwitch<Image> {
+
+	@Override
+	public Image caseEventBObject(final EventBObject object) {
+		// TODO return generic image?
+		return null;
+	}
+
+	@Override
+	public Image caseContext(final Context object) {
+		return Images.getImage(Images.IMG_CONTEXT);
+	}
+
+	@Override
+	public Image caseConstant(final Constant object) {
+		return Images.getImage(Images.IMG_CONSTANT);
+	}
+
+	@Override
+	public Image caseCarrierSet(final CarrierSet object) {
+		return Images.getImage(Images.IMG_CARRIER_SET);
+	}
+
+	@Override
+	public Image caseAxiom(final Axiom object) {
+		return Images.getImage(Images.IMG_AXIOM);
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextLabelSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextLabelSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea8030f1c5dccd7945ffbb4d91675730a86c267d
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextLabelSwitch.java
@@ -0,0 +1,53 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.jface.viewers.StyledString;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.context.Axiom;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.context.util.ContextSwitch;
+
+public class ContextLabelSwitch extends ContextSwitch<StyledString> {
+
+	@Override
+	public StyledString caseEventBNamed(final EventBNamed object) {
+		/*
+		 * Default method for all EventBNamedElements that are not handled
+		 * seperately in their own methods.
+		 */
+		return LabelHelper.getStyledName(object.getName());
+	}
+
+	@Override
+	public StyledString caseContext(final Context object) {
+		final StyledString result = LabelHelper.getStyledName(object.getName());
+
+		final EList<String> extendsNames = object.getExtendsNames();
+		if (extendsNames.size() > 0) {
+			LabelHelper.appendAttrDelim(result);
+			result.append("extends ", LabelHelper.ATTRIBUTE_STYLER);
+			result.append(LabelHelper.joinEList(extendsNames),
+					LabelHelper.ATTRIBUTE_STYLER);
+		}
+
+		return result;
+	}
+
+	@Override
+	public StyledString caseAxiom(final Axiom object) {
+		final StyledString result = LabelHelper.getStyledName(object.getName());
+
+		if (object.isTheorem()) {
+			LabelHelper.appendAttrDelim(result);
+			result.append("theorem", LabelHelper.ATTRIBUTE_STYLER);
+		}
+
+		return result;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextNavigationProvider.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextNavigationProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..229fe40f05df8b0555ccc52dc15eafb40e80aed8
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/ContextNavigationProvider.java
@@ -0,0 +1,66 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eventb.emf.core.EventBCommented;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.util.ContextSwitch;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.model.texttools.TextRange;
+
+public class ContextNavigationProvider extends ContextSwitch<TextRange>
+		implements INavigationProvider {
+
+	public TextRange getHighlightRange(final EventBObject element) {
+		return doSwitch(element);
+	}
+
+	@Override
+	public TextRange caseEventBObject(final EventBObject object) {
+		/*
+		 * Default for all elments that are not explicitely handled. We create a
+		 * new copy here so it's save to change the range.
+		 */
+		return TextPositionUtil.createTextRange(object);
+	}
+
+	@Override
+	public TextRange caseEventBElement(final EventBElement object) {
+		final TextRange range = caseEventBObject(object);
+
+		/*
+		 * Correct offset if there is a comment as we don't want to highlight
+		 * the comment.
+		 */
+		if (object instanceof EventBCommented) {
+			final String comment = ((EventBCommented) object).getComment();
+			if (comment != null && comment.length() > 0) {
+				TextPositionUtil.correctStartOffset(range, comment.length());
+			}
+		}
+
+		return range;
+	}
+
+	@Override
+	public TextRange caseEventBNamed(final EventBNamed object) {
+		TextRange range = TextPositionUtil.getInternalPosition(
+				(EventBElement) object, object.getName());
+
+		if (range == null) {
+			range = caseEventBElement((EventBElement) object);
+
+			if (range != null) {
+				range.setLength(1);
+			}
+		}
+
+		return range;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/INavigationProvider.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/INavigationProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d46edb2110c2b0cb0c5dd9325b70f1de9496c7b
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/INavigationProvider.java
@@ -0,0 +1,14 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eventb.emf.core.EventBObject;
+import org.eventb.texttools.model.texttools.TextRange;
+
+public interface INavigationProvider {
+	public TextRange getHighlightRange(final EventBObject element);
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/LabelHelper.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/LabelHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..68e735ec33909bc0c41d0145ab43473fe8641d31
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/LabelHelper.java
@@ -0,0 +1,52 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.StyledString.Styler;
+import org.eventb.emf.core.EventBNamed;
+
+public class LabelHelper {
+
+	public static final Styler ATTRIBUTE_STYLER = StyledString.QUALIFIER_STYLER;
+	public static final String ATTRIBUTE_DELIMITER = " : ";
+
+	public static StyledString getPlainStyledText(final String contents) {
+		return new StyledString(contents);
+	}
+
+	public static StyledString getStyledName(final String name) {
+		return getPlainStyledText(name);
+	}
+
+	public static <T> String joinEList(final EList<T> parameters) {
+		return StringUtils.join(getAsStringList(parameters), ", ");
+	}
+
+	public static void appendAttrDelim(final StyledString result) {
+		result.append(ATTRIBUTE_DELIMITER, ATTRIBUTE_STYLER);
+	}
+
+	private static <T> List<String> getAsStringList(final EList<T> parameters) {
+		final List<String> result = new ArrayList<String>();
+
+		for (final T element : parameters) {
+			if (element instanceof EventBNamed) {
+				result.add(((EventBNamed) element).getName());
+			} else {
+				result.add(element.toString());
+			}
+		}
+
+		return result;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineImageSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineImageSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d1400386faf917db306b2ae1df63ba94f48caae
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineImageSwitch.java
@@ -0,0 +1,67 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eclipse.swt.graphics.Image;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Guard;
+import org.eventb.emf.core.machine.Invariant;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.Variable;
+import org.eventb.emf.core.machine.util.MachineSwitch;
+import org.eventb.texteditor.ui.Images;
+
+public class MachineImageSwitch extends MachineSwitch<Image> {
+
+	@Override
+	public Image caseEventBObject(final EventBObject object) {
+		// TODO return generic image?
+		return null;
+	}
+
+	@Override
+	public Image caseMachine(final Machine object) {
+		return Images.getImage(Images.IMG_MACHINE);
+	}
+
+	@Override
+	public Image caseVariable(final Variable object) {
+		return Images.getImage(Images.IMG_VARIABLE);
+	}
+
+	@Override
+	public Image caseInvariant(final Invariant object) {
+		if (object.isTheorem()) {
+			return Images.getImage(Images.IMG_INVARIANT);
+		} else {
+			return Images.getImage(Images.IMG_THEOREM);
+		}
+	}
+
+	@Override
+	public Image caseEvent(final Event object) {
+		return Images.getImage(Images.IMG_EVENT);
+	}
+
+	@Override
+	public Image caseGuard(final Guard object) {
+		return Images.getImage(Images.IMG_GUARD);
+	}
+
+	@Override
+	public Image caseAction(final Action object) {
+		return Images.getImage(Images.IMG_ACTION);
+	}
+
+	// TODO find images for these elements
+	// @Override
+	// public Image caseWitness(Witness object) {
+	// return Images.getImage(Images.IMG_wi);
+	// }
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineLabelSwitch.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineLabelSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..4049ed73848a14df007d4e8eb6ab23698a502f85
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineLabelSwitch.java
@@ -0,0 +1,102 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.jface.viewers.StyledString;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Invariant;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.Variant;
+import org.eventb.emf.core.machine.util.MachineSwitch;
+
+public class MachineLabelSwitch extends MachineSwitch<StyledString> {
+
+	@Override
+	public StyledString caseEventBNamed(final EventBNamed object) {
+		/*
+		 * Default method for all EventBNamedElements that are not handled
+		 * seperately in their own methods.
+		 */
+		return LabelHelper.getStyledName(object.getName());
+	}
+
+	@Override
+	public StyledString caseMachine(final Machine object) {
+		final StyledString result = LabelHelper.getStyledName(object.getName());
+
+		final EList<String> refinesNames = object.getRefinesNames();
+		if (refinesNames.size() > 0) {
+			LabelHelper.appendAttrDelim(result);
+			result.append("refines ", LabelHelper.ATTRIBUTE_STYLER);
+			result.append(LabelHelper.joinEList(refinesNames),
+					LabelHelper.ATTRIBUTE_STYLER);
+		}
+
+		final EList<String> seesNames = object.getSeesNames();
+		if (seesNames.size() > 0) {
+			LabelHelper.appendAttrDelim(result);
+			result.append("sees ", LabelHelper.ATTRIBUTE_STYLER);
+			result.append(LabelHelper.joinEList(seesNames),
+					LabelHelper.ATTRIBUTE_STYLER);
+		}
+
+		return result;
+	}
+
+	@Override
+	public StyledString caseInvariant(final Invariant object) {
+		final StyledString result = LabelHelper.getStyledName(object.getName());
+
+		if (object.isTheorem()) {
+			LabelHelper.appendAttrDelim(result);
+			result.append("theorem", LabelHelper.ATTRIBUTE_STYLER);
+		}
+
+		return result;
+	}
+
+	@Override
+	public StyledString caseVariant(final Variant object) {
+		return LabelHelper.getStyledName("Variant");
+	}
+
+	@Override
+	public StyledString caseEvent(final Event object) {
+		final StyledString result = LabelHelper.getStyledName(object.getName());
+
+		if (object.getParameters().size() > 0) {
+			final String paramString = LabelHelper.joinEList(object
+					.getParameters());
+			result.append(" (" + paramString + ")");
+		}
+
+		final EList<String> refinesNames = object.getRefinesNames();
+		if (object.isExtended()) {
+			LabelHelper.appendAttrDelim(result);
+			result.append("extends ", LabelHelper.ATTRIBUTE_STYLER);
+
+			if (refinesNames.size() > 0) {
+				result
+						.append(refinesNames.get(0),
+								LabelHelper.ATTRIBUTE_STYLER);
+			} else {
+				result.append("?", LabelHelper.ATTRIBUTE_STYLER);
+			}
+		} else {
+			if (refinesNames.size() > 0) {
+				LabelHelper.appendAttrDelim(result);
+				result.append("refines ", LabelHelper.ATTRIBUTE_STYLER);
+				result.append(LabelHelper.joinEList(refinesNames),
+						LabelHelper.ATTRIBUTE_STYLER);
+			}
+		}
+
+		return result;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineNavigationProvider.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineNavigationProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..f9c18b8c1881134a91dcec8301c086c405c81457
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/MachineNavigationProvider.java
@@ -0,0 +1,81 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eventb.emf.core.EventBCommented;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.machine.Variant;
+import org.eventb.emf.core.machine.util.MachineSwitch;
+import org.eventb.texttools.Constants;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.model.texttools.TextRange;
+
+public class MachineNavigationProvider extends MachineSwitch<TextRange>
+		implements INavigationProvider {
+
+	public TextRange getHighlightRange(final EventBObject element) {
+		return doSwitch(element);
+	}
+
+	@Override
+	public TextRange caseEventBObject(final EventBObject object) {
+		/*
+		 * Default for all elments that are not explicitely handled. We create a
+		 * new copy here so it's save to change the range.
+		 */
+		return TextPositionUtil.createTextRange(object);
+	}
+
+	@Override
+	public TextRange caseEventBElement(final EventBElement object) {
+		final TextRange range = caseEventBObject(object);
+
+		/*
+		 * Correct offset if there is a comment as we don't want to highlight
+		 * the comment.
+		 */
+		if (object instanceof EventBCommented) {
+			final String comment = ((EventBCommented) object).getComment();
+
+			if (comment != null && comment.length() > 0) {
+				TextPositionUtil.correctStartOffset(range, comment.length());
+			}
+		}
+
+		return range;
+	}
+
+	@Override
+	public TextRange caseVariant(final Variant object) {
+		final TextRange range = caseEventBElement(object);
+
+		if (range != null) {
+			TextPositionUtil.correctStartOffset(range, Constants.VARIANT
+					.length());
+		}
+
+		return range;
+	}
+
+	@Override
+	public TextRange caseEventBNamed(final EventBNamed object) {
+		TextRange range = TextPositionUtil.getInternalPosition(
+				(EventBElement) object, object.getName());
+
+		if (range == null) {
+			range = caseEventBElement((EventBElement) object);
+
+			if (range != null) {
+				range.setLength(1);
+			}
+		}
+
+		return range;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/OutlineLabelProvider.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/OutlineLabelProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..05a23280dfa2dd9ea0fde224990841f9b73024d5
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/OutlineLabelProvider.java
@@ -0,0 +1,95 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.ContextPackage;
+import org.eventb.emf.core.machine.MachinePackage;
+
+public class OutlineLabelProvider extends LabelProvider implements
+		IStyledLabelProvider {
+
+	private MachineLabelSwitch machineLabelSwitch;
+	private ContextLabelSwitch contextLabelSwitch;
+	private MachineImageSwitch machineImageSwitch;
+	private ContextImageSwitch contextImageSwitch;
+
+	@Override
+	public String getText(final Object element) {
+		return getStyledText(element).getString();
+	}
+
+	public StyledString getStyledText(final Object element) {
+		if (element instanceof EventBObject) {
+			final EventBObject emfObject = (EventBObject) element;
+			final String packageNsURI = emfObject.eClass().getEPackage()
+					.getNsURI();
+
+			if (packageNsURI.equals(MachinePackage.eNS_URI)) {
+				return getTextsForMachine(emfObject);
+			} else if (packageNsURI.equals(ContextPackage.eNS_URI)) {
+				return getTextsForContext(emfObject);
+			}
+		}
+
+		// we can only handle EventBObjects here, use default otherwise
+		return new StyledString(super.getText(element));
+	}
+
+	@Override
+	public Image getImage(final Object element) {
+		if (element instanceof EventBObject) {
+			final EventBObject emfObject = (EventBObject) element;
+			final String packageNsURI = emfObject.eClass().getEPackage()
+					.getNsURI();
+
+			if (packageNsURI.equals(MachinePackage.eNS_URI)) {
+				return getImageForMachine(emfObject);
+			} else if (packageNsURI.equals(ContextPackage.eNS_URI)) {
+				return getImageForContext(emfObject);
+			}
+		}
+
+		return super.getImage(element);
+	}
+
+	private Image getImageForMachine(final EventBObject emfObject) {
+		if (machineImageSwitch == null) {
+			machineImageSwitch = new MachineImageSwitch();
+		}
+
+		return machineImageSwitch.doSwitch(emfObject);
+	}
+
+	private Image getImageForContext(final EventBObject emfObject) {
+		if (contextImageSwitch == null) {
+			contextImageSwitch = new ContextImageSwitch();
+		}
+
+		return contextImageSwitch.doSwitch(emfObject);
+	}
+
+	private StyledString getTextsForContext(final EventBObject emfObject) {
+		if (contextLabelSwitch == null) {
+			contextLabelSwitch = new ContextLabelSwitch();
+		}
+
+		return contextLabelSwitch.doSwitch(emfObject);
+	}
+
+	private StyledString getTextsForMachine(final EventBObject emfObject) {
+		if (machineLabelSwitch == null) {
+			machineLabelSwitch = new MachineLabelSwitch();
+		}
+
+		return machineLabelSwitch.doSwitch(emfObject);
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/OutlinePage.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/OutlinePage.java
new file mode 100644
index 0000000000000000000000000000000000000000..21f350652f91b595e46996ecde4cae12aad8bf48
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/OutlinePage.java
@@ -0,0 +1,36 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
+import org.eventb.texteditor.ui.editor.EventBTextEditor;
+
+public class OutlinePage extends ContentOutlinePage {
+
+	private final EventBTextEditor editor;
+
+	public OutlinePage(final EventBTextEditor editor) {
+		this.editor = editor;
+	}
+
+	@Override
+	public void createControl(final Composite parent) {
+		super.createControl(parent);
+
+		final TreeViewer viewer = getTreeViewer();
+		viewer.setContentProvider(new ContentProvider());
+		viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
+				new OutlineLabelProvider()));
+		viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
+
+		viewer.setInput(editor.getEditorInput());
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/RevealSelectionListener.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/RevealSelectionListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..e79f403aecd4b1fd5cedd20b0caee6bec30dcd72
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/outline/RevealSelectionListener.java
@@ -0,0 +1,62 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.outline;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.ContextPackage;
+import org.eventb.emf.core.machine.MachinePackage;
+import org.eventb.texttools.model.texttools.TextRange;
+
+public class RevealSelectionListener implements ISelectionChangedListener {
+	private final INavigationProvider machineNavProvider = new MachineNavigationProvider();
+	private final INavigationProvider contextNavProvider = new ContextNavigationProvider();
+	private final TextEditor editor;
+
+	public RevealSelectionListener(final TextEditor editor) {
+		this.editor = editor;
+	}
+
+	public void selectionChanged(final SelectionChangedEvent event) {
+		final ISelection selection = event.getSelection();
+		if (!(selection instanceof IStructuredSelection)) {
+			return;
+		}
+
+		final IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+		if (structuredSelection.size() != 1) {
+			return;
+		}
+
+		final Object firstElement = structuredSelection.getFirstElement();
+		if (!(firstElement instanceof EventBObject)) {
+			return;
+		}
+
+		highlightElement((EventBObject) firstElement);
+	}
+
+	private void highlightElement(final EventBObject element) {
+		TextRange range = null;
+
+		final String packageNsURI = element.eClass().getEPackage().getNsURI();
+
+		if (packageNsURI.equals(MachinePackage.eNS_URI)) {
+			range = machineNavProvider.getHighlightRange(element);
+		} else if (packageNsURI.equals(ContextPackage.eNS_URI)) {
+			range = contextNavProvider.getHighlightRange(element);
+		}
+
+		if (range != null) {
+			editor.selectAndReveal(range.getOffset(), range.getLength());
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/HighlightingPreferencePage.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/HighlightingPreferencePage.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffcc40a049d4b44afb7b6ba63dae89db8c741e66
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/HighlightingPreferencePage.java
@@ -0,0 +1,186 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.preferences;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.preference.ColorSelector;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eventb.texteditor.ui.TextDecoration;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.TextDecoration.ESyntaxElement;
+
+public class HighlightingPreferencePage extends PreferencePage implements
+		IWorkbenchPreferencePage {
+
+	private final List<Field> fields = new ArrayList<Field>();
+
+	public HighlightingPreferencePage() {
+		super();
+		setPreferenceStore(TextEditorPlugin.getPlugin().getPreferenceStore());
+		setDescription("Please configure the syntax highlighting for the Event-B TextEditor. You can select the color and the style for each element type.");
+	}
+
+	@Override
+	protected Control createContents(final Composite parent) {
+		final Composite colorComposite = new Composite(parent, SWT.NONE);
+		final GridLayout layout = new GridLayout();
+		layout.numColumns = 1;
+		layout.marginHeight = 0;
+		layout.marginWidth = 0;
+		colorComposite.setLayout(layout);
+
+		for (final ESyntaxElement element : TextDecoration.ESyntaxElement
+				.values()) {
+			createField(element, colorComposite);
+		}
+
+		loadPreferences();
+
+		colorComposite.layout(false);
+		return colorComposite;
+	}
+
+	private void savePreferences() {
+		final IPreferenceStore store = getPreferenceStore();
+
+		for (final Field f : fields) {
+			final ESyntaxElement element = f.element;
+
+			final int bold = f.boldButton.getSelection() ? SWT.BOLD : SWT.NONE;
+			final int italic = f.italicButton.getSelection() ? SWT.ITALIC
+					: SWT.NONE;
+			final int underline = f.underlineButton.getSelection() ? TextAttribute.UNDERLINE
+					: SWT.NONE;
+			final int style = bold | italic | underline;
+
+			PreferenceConverter.setValue(store, element.getColorKey(),
+					f.colorSelector.getColorValue());
+			store.setValue(element.getStyleKey(), style);
+
+			element.resetToken();
+		}
+	}
+
+	private void loadPreferences() {
+		final IPreferenceStore store = getPreferenceStore();
+
+		for (final Field f : fields) {
+			final ESyntaxElement element = f.element;
+			final RGB color = PreferenceConverter.getColor(store, element
+					.getColorKey());
+			final int style = store.getInt(element.getStyleKey());
+
+			setFieldValues(f, color, style);
+		}
+	}
+
+	private void loadDefaultPreferences() {
+		final IPreferenceStore store = getPreferenceStore();
+
+		for (final Field f : fields) {
+			final ESyntaxElement element = f.element;
+			final RGB color = PreferenceConverter.getDefaultColor(store,
+					element.getColorKey());
+			final int style = store.getDefaultInt(element.getStyleKey());
+
+			setFieldValues(f, color, style);
+		}
+	}
+
+	private void setFieldValues(final Field f, final RGB color, final int style) {
+		f.colorSelector.setColorValue(color);
+		f.boldButton.setSelection((style & SWT.BOLD) == SWT.BOLD);
+		f.italicButton.setSelection((style & SWT.ITALIC) == SWT.ITALIC);
+		f.underlineButton
+				.setSelection((style & TextAttribute.UNDERLINE) == TextAttribute.UNDERLINE);
+	}
+
+	private void createField(final ESyntaxElement element,
+			final Composite parent) {
+		final Composite field = new Composite(parent, SWT.NONE);
+
+		final GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+		gridData.grabExcessHorizontalSpace = true;
+
+		final GridLayout layout = new GridLayout(5, false);
+		// layout.marginHeight = 5;
+		layout.marginWidth = 5;
+		field.setLayout(layout);
+		field.setLayoutData(gridData);
+
+		final Label label = new Label(field, SWT.NONE);
+		label.setText(element.getLabel());
+		label.setLayoutData(gridData);
+
+		final ColorSelector colorSelector = new ColorSelector(field);
+
+		final Button boldButton = new Button(field, SWT.CHECK);
+		boldButton.setText("Bold");
+
+		final Button italicButton = new Button(field, SWT.CHECK);
+		italicButton.setText("Italic");
+
+		final Button underlineButton = new Button(field, SWT.CHECK);
+		underlineButton.setText("Underline");
+
+		fields.add(new Field(element, colorSelector, boldButton, italicButton,
+				underlineButton));
+	}
+
+	@Override
+	public boolean performOk() {
+		savePreferences();
+
+		// TODO find active editor and trigger reconcilePresentation on
+		// EventBPresentationReconciler
+
+		return true;
+	}
+
+	@Override
+	protected void performDefaults() {
+		loadDefaultPreferences();
+		super.performDefaults();
+	}
+
+	public void init(final IWorkbench workbench) {
+		// TODO Auto-generated method stub
+	}
+
+	private class Field {
+		protected final ESyntaxElement element;
+		protected final ColorSelector colorSelector;
+		protected final Button boldButton;
+		protected final Button italicButton;
+		protected final Button underlineButton;
+
+		protected Field(final ESyntaxElement element,
+				final ColorSelector colorSelector, final Button boldButton,
+				final Button italicButton, final Button underlineButton) {
+			this.element = element;
+			this.colorSelector = colorSelector;
+			this.boldButton = boldButton;
+			this.italicButton = italicButton;
+			this.underlineButton = underlineButton;
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/PreferenceInitializer.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/PreferenceInitializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..96efdb5ec94d17c9be922f23d72fc5ecc4ab3797
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/PreferenceInitializer.java
@@ -0,0 +1,58 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.preferences;
+
+import static org.eclipse.swt.SWT.*;
+import static org.eventb.texteditor.ui.TextDecoration.ESyntaxElement.*;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.TextDecoration.ESyntaxElement;
+
+/**
+ * Class used to initialize default preference values.
+ */
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+
+	private IPreferenceStore store;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @seeorg.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#
+	 * initializeDefaultPreferences()
+	 */
+	@Override
+	public void initializeDefaultPreferences() {
+		store = TextEditorPlugin.getPlugin().getPreferenceStore();
+
+		setDefaultValues(Comment, COLOR_GRAY, ITALIC);
+		setDefaultValues(Label, COLOR_DARK_GRAY, NORMAL);
+		setDefaultValues(Keyword, COLOR_DARK_RED, BOLD);
+		setDefaultValues(MathKeyword, COLOR_DARK_RED, NORMAL);
+		setDefaultValues(GlobalVariable, COLOR_BLUE, NORMAL);
+		setDefaultValues(BoundedVariable, COLOR_DARK_GREEN, ITALIC);
+		setDefaultValues(Parameter, COLOR_BLUE, ITALIC);
+		setDefaultValues(Constant, COLOR_BLUE, BOLD);
+		setDefaultValues(Set, COLOR_DARK_GREEN, BOLD);
+	}
+
+	private void setDefaultValues(final ESyntaxElement element,
+			final int color, final int style) {
+		PreferenceConverter.setDefault(store, element.getColorKey(),
+				getRGB(color));
+		store.setDefault(element.getStyleKey(), style);
+	}
+
+	private static RGB getRGB(final int colorCode) {
+		return Display.getDefault().getSystemColor(colorCode).getRGB();
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/TemplatePrefPage.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/TemplatePrefPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..af544dce3638128daa6d206b1e6be9577ae3cd88
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/TemplatePrefPage.java
@@ -0,0 +1,29 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.preferences;
+
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.texteditor.templates.TemplatePreferencePage;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+
+public class TemplatePrefPage extends TemplatePreferencePage {
+
+	@Override
+	public void init(final IWorkbench workbench) {
+		super.init(workbench);
+
+		setTemplateStore(TextEditorPlugin.getPlugin().getTemplateStore());
+		setContextTypeRegistry(TextEditorPlugin.getPlugin()
+				.getContextTypeRegistry());
+		setPreferenceStore(TextEditorPlugin.getPlugin().getPreferenceStore());
+	}
+
+	@Override
+	protected boolean isShowFormatterSetting() {
+		return false;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/TextEditorPreferencePage.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/TextEditorPreferencePage.java
new file mode 100644
index 0000000000000000000000000000000000000000..e6dbabb62f62127f7b498c4e78de6aaa0404813a
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/preferences/TextEditorPreferencePage.java
@@ -0,0 +1,28 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.preferences;
+
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class TextEditorPreferencePage extends FieldEditorPreferencePage
+		implements IWorkbenchPreferencePage {
+	public TextEditorPreferencePage() {
+		super();
+		setDescription("This section provides preferences for the Event-B TextEditor.");
+	}
+
+	@Override
+	protected void createFieldEditors() {
+		// nothing for now
+	}
+
+	public void init(final IWorkbench workbench) {
+		// nothing for now
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/CommentTokenScanner.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/CommentTokenScanner.java
new file mode 100644
index 0000000000000000000000000000000000000000..826338e7e0c2eef555b60df67f3ba78d528ea8a8
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/CommentTokenScanner.java
@@ -0,0 +1,27 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler;
+
+import org.eclipse.jface.text.rules.EndOfLineRule;
+import org.eclipse.jface.text.rules.IRule;
+import org.eclipse.jface.text.rules.MultiLineRule;
+import org.eclipse.jface.text.rules.RuleBasedScanner;
+import org.eclipse.jface.text.rules.Token;
+import org.eventb.texteditor.ui.TextDecoration.ESyntaxElement;
+
+public class CommentTokenScanner extends RuleBasedScanner {
+
+	public CommentTokenScanner() {
+		super();
+
+		final IRule[] rules = new IRule[2];
+		final Token token = ESyntaxElement.Comment.getToken();
+		rules[0] = new EndOfLineRule("//", token);
+		rules[1] = new MultiLineRule("/*", "*/", token, (char) 0, true);
+		setRules(rules);
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/EventBPresentationReconciler.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/EventBPresentationReconciler.java
new file mode 100644
index 0000000000000000000000000000000000000000..862f0e95c4df3fa296d8fc50ea284cf0f0ad79c5
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/EventBPresentationReconciler.java
@@ -0,0 +1,131 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler;
+
+import java.util.Set;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.presentation.PresentationReconciler;
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.build.dom.DomManager;
+import org.eventb.texteditor.ui.build.dom.IComponentDom;
+import org.eventb.texteditor.ui.build.dom.IDomChangeListener;
+import org.eventb.texteditor.ui.reconciler.partitioning.PartitionScanner;
+
+/**
+ * This {@link PresentationReconciler} does basic syntax highlighting based on
+ * content types of partitions. It reset text to default style if default
+ * content type is found. Beside that it recognizes the following elements in
+ * EventB components:
+ * <ul>
+ * <li>Comment</li>
+ * <li>Label</li>
+ * <li>Structural keywords</li>
+ * <li>Formula keywords</li>
+ * </ul>
+ * 
+ */
+public class EventBPresentationReconciler extends PresentationReconciler
+		implements IDomChangeListener {
+	private static final DomManager domManager = TextEditorPlugin
+			.getDomManager();
+	private SemanticTokenScanner semanticScanner;
+	private ITextViewer viewer;
+	private IComponentDom dom = null;
+	private Resource resource;
+
+	public EventBPresentationReconciler() {
+		super();
+		init();
+	}
+
+	private void init() {
+		// init semantic token scanner
+		semanticScanner = new SemanticTokenScanner();
+
+		// default content type: semantic highlighing
+		DefaultDamagerRepairer damager = new DefaultDamagerRepairer(
+				semanticScanner);
+		setDamager(damager, IDocument.DEFAULT_CONTENT_TYPE);
+		setRepairer(damager, IDocument.DEFAULT_CONTENT_TYPE);
+
+		// comments
+		damager = new DefaultDamagerRepairer(new CommentTokenScanner());
+		setDamager(damager, PartitionScanner.CONTENT_TYPE_COMMENT);
+		setRepairer(damager, PartitionScanner.CONTENT_TYPE_COMMENT);
+
+		// labels
+		damager = new DefaultDamagerRepairer(new LabelTokenScanner());
+		setDamager(damager, PartitionScanner.CONTENT_TYPE_LABEL);
+		setRepairer(damager, PartitionScanner.CONTENT_TYPE_LABEL);
+	}
+
+	public void reconcilePresentation() {
+		if (viewer == null && viewer.getTextWidget() == null
+				&& viewer.getTextWidget().isDisposed()) {
+			return;
+		}
+
+		viewer.getTextWidget().getDisplay().asyncExec(new Runnable() {
+			public void run() {
+				viewer.invalidateTextPresentation();
+			}
+		});
+	}
+
+	@Override
+	public void install(final ITextViewer viewer) {
+		super.install(viewer);
+		this.viewer = viewer;
+		domManager.addDomChangeListener(this);
+	}
+
+	@Override
+	public void uninstall() {
+		domManager.removeDomChangeListener(this);
+		super.uninstall();
+
+		setInputResource(null);
+	}
+
+	public void setInputResource(final Resource input) {
+		resource = input;
+
+		if (input != null) {
+			dom = domManager.getDom(input);
+		} else {
+			dom = null;
+		}
+
+		semanticScanner.setInputResource(input);
+	}
+
+	public void domChanged(final IComponentDom changedDom) {
+		checkInit();
+
+		// we are directly affected by the update
+		if (changedDom == dom) {
+			reconcilePresentation();
+		} else if (dom != null) {
+			// any (transitive) relation to the changed dom?
+			final Set<IComponentDom> referencedDoms = dom
+					.getReferencedDoms(true);
+			if (referencedDoms.contains(changedDom)) {
+				reconcilePresentation();
+			}
+		}
+	}
+
+	private void checkInit() {
+		if (dom == null && resource != null) {
+			dom = domManager.getDom(resource);
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/IdentifierDetector.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/IdentifierDetector.java
new file mode 100644
index 0000000000000000000000000000000000000000..669807e224f1050b8fb9a0198015fda2020d4e3d
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/IdentifierDetector.java
@@ -0,0 +1,19 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler;
+
+import org.eclipse.jface.text.rules.IWordDetector;
+
+public class IdentifierDetector implements IWordDetector {
+	public boolean isWordStart(final char c) {
+		return Character.isJavaIdentifierStart(c);
+	}
+
+	public boolean isWordPart(final char c) {
+		return Character.isJavaIdentifierPart(c) || c == '\'';
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/IdentifierRule.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/IdentifierRule.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f3e3cfd0d07c62c7756c869481e352faac2fc81
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/IdentifierRule.java
@@ -0,0 +1,143 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler;
+
+import org.eclipse.jface.text.rules.ICharacterScanner;
+import org.eclipse.jface.text.rules.IRule;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.IWordDetector;
+import org.eclipse.jface.text.rules.Token;
+import org.eventb.texteditor.ui.TextDecoration.ESyntaxElement;
+import org.eventb.texteditor.ui.build.dom.IDom;
+
+public class IdentifierRule implements IRule {
+
+	/** The word detector used by this rule. */
+	private final IWordDetector fDetector = new IdentifierDetector();
+
+	/**
+	 * The default token to be returned on success and if nothing else has been
+	 * specified.
+	 */
+	private final IToken fDefaultToken = new Token(null);
+
+	/** Buffer used for pattern detection. */
+	private final StringBuffer fBuffer = new StringBuffer();
+
+	private int offset;
+
+	private IDom dom;
+
+	public IdentifierRule() {
+	}
+
+	/*
+	 * @see IRule#evaluate(ICharacterScanner)
+	 */
+	public IToken evaluate(final ICharacterScanner scanner) {
+		if (dom != null) {
+			// read next word
+			readIdentifier(scanner);
+
+			if (fBuffer.length() > 0) {
+				final String buffer = fBuffer.toString();
+
+				// determine type for identifier
+				IDom.IdentifierType type = null;
+				final IDom scope = dom.getScopingDom(offset);
+				if (scope != null) {
+					type = scope.getIdentifierType(buffer);
+				}
+
+				// resolve text attribute
+				final IToken token = getToken(type);
+				if (token != null) {
+					return token;
+				}
+
+				if (fDefaultToken.isUndefined()) {
+					unreadBuffer(scanner);
+				}
+
+				return fDefaultToken;
+			}
+		}
+
+		return Token.UNDEFINED;
+	}
+
+	private void readIdentifier(final ICharacterScanner scanner) {
+		fBuffer.setLength(0);
+
+		if (newIdentifierBegins(scanner)) {
+			int c = scanner.read();
+
+			if (c != ICharacterScanner.EOF && fDetector.isWordStart((char) c)) {
+				do {
+					fBuffer.append((char) c);
+					c = scanner.read();
+				} while (c != ICharacterScanner.EOF
+						&& fDetector.isWordPart((char) c));
+				scanner.unread();
+			} else {
+				scanner.unread();
+			}
+		}
+	}
+
+	private boolean newIdentifierBegins(final ICharacterScanner scanner) {
+
+		// read char before offset
+		scanner.unread();
+		final int c = scanner.read();
+
+		if (fDetector.isWordStart((char) c) || fDetector.isWordPart((char) c)) {
+			return false;
+		}
+
+		return true;
+	}
+
+	private IToken getToken(final IDom.IdentifierType type) {
+		if (type != null) {
+			switch (type) {
+			case GlobalVariable:
+				return ESyntaxElement.GlobalVariable.getToken();
+			case LocalVariable:
+				return ESyntaxElement.BoundedVariable.getToken();
+			case Parameter:
+				return ESyntaxElement.Parameter.getToken();
+			case Constant:
+				return ESyntaxElement.Constant.getToken();
+			case Set:
+				return ESyntaxElement.Set.getToken();
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Returns the characters in the buffer to the scanner.
+	 * 
+	 * @param scanner
+	 *            the scanner to be used
+	 */
+	protected void unreadBuffer(final ICharacterScanner scanner) {
+		for (int i = fBuffer.length() - 1; i >= 0; i--) {
+			scanner.unread();
+		}
+	}
+
+	public void setOffset(final int offset) {
+		this.offset = offset;
+	}
+
+	public void setDom(final IDom dom) {
+		this.dom = dom;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/KeywordTokenScanner.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/KeywordTokenScanner.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d22b6e1ecb09628afda612f74586503a670453d
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/KeywordTokenScanner.java
@@ -0,0 +1,32 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler;
+
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.rules.IRule;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.RuleBasedScanner;
+import org.eclipse.jface.text.rules.Token;
+import org.eventb.texteditor.ui.reconciler.partitioning.KeywordDetector;
+import org.eventb.texteditor.ui.reconciler.partitioning.WordPredicateRule;
+
+public class KeywordTokenScanner extends RuleBasedScanner {
+	public KeywordTokenScanner(final String[] keywords,
+			final TextAttribute textAttribute) {
+		super();
+
+		final IToken token = new Token(textAttribute);
+		final WordPredicateRule keywordRule = new WordPredicateRule(
+				new KeywordDetector());
+
+		for (final String keyword : keywords) {
+			keywordRule.addWord(keyword, token);
+		}
+
+		setRules(new IRule[] { keywordRule });
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/LabelTokenScanner.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/LabelTokenScanner.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9d4e50446d0de1a4e5a5636c3d23eef9c91f022
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/LabelTokenScanner.java
@@ -0,0 +1,22 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler;
+
+import org.eclipse.jface.text.rules.IRule;
+import org.eclipse.jface.text.rules.RuleBasedScanner;
+import org.eclipse.jface.text.rules.WordRule;
+import org.eventb.texteditor.ui.TextDecoration.ESyntaxElement;
+import org.eventb.texteditor.ui.reconciler.partitioning.LabelDetector;
+
+public class LabelTokenScanner extends RuleBasedScanner {
+	public LabelTokenScanner() {
+		super();
+
+		setRules(new IRule[] { new WordRule(new LabelDetector(),
+				ESyntaxElement.Label.getToken(), true) });
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/SemanticTokenScanner.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/SemanticTokenScanner.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8f742eb33bb1538f982a2c1518b98c2bd874bd1
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/SemanticTokenScanner.java
@@ -0,0 +1,115 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.jface.text.rules.IRule;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.RuleBasedScanner;
+import org.eclipse.jface.text.rules.Token;
+import org.eventb.texteditor.ui.TextEditorPlugin;
+import org.eventb.texteditor.ui.TextDecoration.ESyntaxElement;
+import org.eventb.texteditor.ui.build.dom.DomManager;
+import org.eventb.texteditor.ui.build.dom.IComponentDom;
+import org.eventb.texteditor.ui.reconciler.partitioning.KeywordDetector;
+import org.eventb.texteditor.ui.reconciler.partitioning.WordPredicateRule;
+import org.eventb.texttools.Constants;
+
+public class SemanticTokenScanner extends RuleBasedScanner {
+	private final DomManager domManager = TextEditorPlugin.getDomManager();
+	private final IdentifierRule identifierRule;
+	private IComponentDom currentDom;
+	private Resource resource;
+
+	public SemanticTokenScanner() {
+		super();
+		setRules(getRules());
+		identifierRule = new IdentifierRule();
+	}
+
+	private IRule[] getRules() {
+		final List<IRule> rules = new ArrayList<IRule>();
+
+		// rule for structural keywords
+		final WordPredicateRule structKWRule = new WordPredicateRule(
+				new KeywordDetector());
+		for (final String keyword : Constants.structural_keywords) {
+			structKWRule.addWord(keyword, ESyntaxElement.Keyword.getToken());
+		}
+		rules.add(structKWRule);
+
+		// rule for formula keywords
+		final WordPredicateRule formulaKwRule = new WordPredicateRule(
+				new KeywordDetector());
+		for (final String keyword : Constants.formula_keywords) {
+			formulaKwRule.addWord(keyword, ESyntaxElement.MathKeyword
+					.getToken());
+		}
+		rules.add(formulaKwRule);
+
+		return rules.toArray(new IRule[rules.size()]);
+	}
+
+	@Override
+	public IToken nextToken() {
+		final int startOffset = fOffset;
+
+		final IToken nextToken = super.nextToken();
+		if (acceptToken(nextToken)) {
+			return nextToken;
+		}
+
+		fTokenOffset = startOffset;
+		fOffset = startOffset;
+		fColumn = UNDEFINED;
+
+		checkInit();
+		identifierRule.setOffset(fOffset);
+		final IToken token = identifierRule.evaluate(this);
+
+		if (!token.isUndefined()) {
+			return token;
+		}
+
+		if (read() == EOF) {
+			return Token.EOF;
+		}
+
+		return fDefaultReturnToken;
+	}
+
+	private boolean acceptToken(final IToken nextToken) {
+		/*
+		 * If the next token is part of an identifier too (e.g. a event name)
+		 * don't accept this token.
+		 */
+		final int c = read();
+		if (Character.isJavaIdentifierPart(c) || c == '\'') {
+			unread();
+			return false;
+		}
+
+		return nextToken == ESyntaxElement.Keyword.getToken()
+				|| nextToken == ESyntaxElement.MathKeyword.getToken()
+				|| nextToken.isEOF();
+	}
+
+	public void setInputResource(final Resource resource) {
+		this.resource = resource;
+		currentDom = null;
+	}
+
+	private void checkInit() {
+		if (currentDom == null && resource != null) {
+			currentDom = domManager.getDom(resource);
+			identifierRule.setDom(currentDom);
+		}
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/KeywordDetector.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/KeywordDetector.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e368897e0b18c5cd2aadf3f1c1bf7b69bd895f3
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/KeywordDetector.java
@@ -0,0 +1,20 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler.partitioning;
+
+import org.eclipse.jface.text.rules.IWordDetector;
+
+public class KeywordDetector implements IWordDetector {
+	public boolean isWordStart(final char c) {
+		return Character.isLetter(c) || c == '\u2115' || c == '\u2119'
+				|| c == '\u2124';
+	}
+
+	public boolean isWordPart(final char c) {
+		return Character.isLetterOrDigit(c) || c == '\u0031';
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/LabelDetector.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/LabelDetector.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b7936117a5201b00bb0a8909745bdfcdf553d06
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/LabelDetector.java
@@ -0,0 +1,19 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler.partitioning;
+
+import org.eclipse.jface.text.rules.IWordDetector;
+
+public class LabelDetector implements IWordDetector {
+	public boolean isWordStart(final char c) {
+		return c == '@';
+	}
+
+	public boolean isWordPart(final char c) {
+		return !Character.isWhitespace(c);
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/PartitionScanner.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/PartitionScanner.java
new file mode 100644
index 0000000000000000000000000000000000000000..2da75a89256afd91427c2cf1e4acc7074cfe1c61
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/PartitionScanner.java
@@ -0,0 +1,63 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler.partitioning;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.rules.EndOfLineRule;
+import org.eclipse.jface.text.rules.IPredicateRule;
+import org.eclipse.jface.text.rules.MultiLineRule;
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.jface.text.rules.WordPatternRule;
+
+public class PartitionScanner extends RuleBasedPartitionScanner {
+
+	private static final String PREFIX = "content_type_";
+	public static final String CONTENT_TYPE_STRUCTURAL_KEYWORD = PREFIX
+			+ "structural_keyword";
+	public static final String CONTENT_TYPE_FORMULA_KEYWORD = PREFIX
+			+ "formula_keyword";
+	public static final String CONTENT_TYPE_COMMENT = PREFIX + "comment";
+	public static final String CONTENT_TYPE_LABEL = PREFIX + "label";
+
+	public static final String[] CONTENT_TYPES = {
+			IDocument.DEFAULT_CONTENT_TYPE, CONTENT_TYPE_STRUCTURAL_KEYWORD,
+			CONTENT_TYPE_FORMULA_KEYWORD, CONTENT_TYPE_COMMENT,
+			CONTENT_TYPE_LABEL };
+
+	public static final Token TOKEN_STRUCTURAL_KEYWORD = new Token(
+			CONTENT_TYPE_STRUCTURAL_KEYWORD);
+	public static final Token TOKEN_FORMULA_KEYWORD = new Token(
+			CONTENT_TYPE_FORMULA_KEYWORD);
+	public static final Token TOKEN_COMMENT = new Token(CONTENT_TYPE_COMMENT);
+	public static final Token TOKEN_LABEL = new Token(CONTENT_TYPE_LABEL);
+
+	public PartitionScanner() {
+		super();
+
+		final List<IPredicateRule> rules = getRules();
+		final IPredicateRule[] result = new IPredicateRule[rules.size()];
+		rules.toArray(result);
+		setPredicateRules(result);
+	}
+
+	private List<IPredicateRule> getRules() {
+		final List<IPredicateRule> rules = new ArrayList<IPredicateRule>();
+		// comment rules
+		rules.add(new EndOfLineRule("//", TOKEN_COMMENT));
+		rules.add(new MultiLineRule("/*", "*/", TOKEN_COMMENT, (char) 0, true));
+
+		// label rule
+		rules.add(new WordPatternRule(new LabelDetector(), "@", null,
+				PartitionScanner.TOKEN_LABEL));
+
+		return rules;
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/Partitioner.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/Partitioner.java
new file mode 100644
index 0000000000000000000000000000000000000000..020e1d819d438bcd4a4086e27f014e65bf2db14a
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/Partitioner.java
@@ -0,0 +1,21 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler.partitioning;
+
+import org.eclipse.jface.text.rules.FastPartitioner;
+
+public class Partitioner extends FastPartitioner {
+
+	public Partitioner(final PartitionScanner partitionScanner,
+			final String[] contentTypes) {
+		super(partitionScanner, contentTypes);
+	}
+
+	public void clearCache() {
+		clearPositionCache();
+	}
+}
diff --git a/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/WordPredicateRule.java b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/WordPredicateRule.java
new file mode 100644
index 0000000000000000000000000000000000000000..296b1ee07aa16f54f7f5edb93b10c2691b2f8e70
--- /dev/null
+++ b/org.eventb.texteditor.ui/src/org/eventb/texteditor/ui/reconciler/partitioning/WordPredicateRule.java
@@ -0,0 +1,47 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texteditor.ui.reconciler.partitioning;
+
+import org.eclipse.jface.text.rules.ICharacterScanner;
+import org.eclipse.jface.text.rules.IPredicateRule;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.IWordDetector;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.jface.text.rules.WordRule;
+
+public class WordPredicateRule extends WordRule implements IPredicateRule {
+
+	public WordPredicateRule(final IWordDetector _detector) {
+		super(_detector);
+	}
+
+	public IToken getSuccessToken() {
+		return fDefaultToken;
+	}
+
+	@Override
+	public IToken evaluate(final ICharacterScanner scanner) {
+		// read char before offset
+		scanner.unread();
+		final int c = scanner.read();
+
+		if (fDetector.isWordStart((char) c) || fDetector.isWordPart((char) c)) {
+			return Token.UNDEFINED;
+		}
+
+		return super.evaluate(scanner);
+	}
+
+	public IToken evaluate(final ICharacterScanner _scanner,
+			final boolean _resume) {
+		return evaluate(_scanner);
+	}
+
+	public void clearWords() {
+		fWords.clear();
+	}
+}
diff --git a/org.eventb.texteditor.ui/templates.xml b/org.eventb.texteditor.ui/templates.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6e175646ffd8101fb3aba42c30cb9a93bb155270
--- /dev/null
+++ b/org.eventb.texteditor.ui/templates.xml
@@ -0,0 +1,424 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<templates>
+
+<!--
+<template
+	id=""
+	name=""
+	description=""
+	context="org.eventb.texteditor."
+	enabled=""
+	></template>
+
+contexts:
+org.eventb.texteditor.anywhere
+org.eventb.texteditor.machine
+org.eventb.texteditor.events
+org.eventb.texteditor.context
+-->
+
+<!--
+ ######## MACHINE context ######## 
+-->
+<template
+	id="machine"
+	name="machine"
+	description="Basic machine structure"
+	context="org.eventb.texteditor.machine"
+	enabled="true"
+	>machine ${Machine_Name}&#xD;variables ${var}&#xD;invariants @${inv1} ${var} : ${type}&#xD;events&#xD;&#x9;event INITIALISATION&#xD;&#x9;then&#xD;&#x9;&#x9;@${act1} ${var} := ${value}&#xD;&#x9;end&#xD;&#xD;&#x9;event ${Eventname}&#xD;&#x9;end&#xD;&#xD;end</template>
+
+<!--
+ ######## CONTEXT context ######## 
+-->
+<template
+	id="context"
+	name="context"
+	description="Basic context structure"
+	context="org.eventb.texteditor.context"
+	enabled="true"
+	>context ${Context_Name}&#xD;&#xD;end</template>
+
+<!--
+ ######## EVENT context ######## 
+-->
+
+<template
+	id="event_minimal"
+	name="eventstructure (minimal)"
+	description="Basic event structure"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>event ${Eventname}&#xD;&#x9;&#x9;then @${act1} ${var}:=${param}&#xD;&#x9;end</template>
+
+<template
+	id="event_parameter"
+	name="eventstructure (with parameter)"
+	description="Event (with parameter) structure"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>event ${Eventname}&#xD;&#x9;&#x9;any ${param}&#xD;&#x9;&#x9;where @${guard} ${param} : ${type}&#xD;&#x9;&#x9;then @${act1} ${var}:=${param}&#xD;&#x9;end&#xD;</template>
+
+<template
+	id="event_full"
+	name="eventstructure (complete)"
+	description="Complete event structure"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>event ${Eventname}&#xD;&#x9;&#x9;refines ${RefinedEvent}&#xD;&#x9;&#x9;any ${param}&#xD;&#x9;&#x9;where @${guard} ${param} : ${type}&#xD;&#x9;&#x9;with @${y} ${param} = ${y}&#xD;&#x9;&#x9;then @${act1} ${var}:=${param}&#xD;&#x9;end</template>
+
+<template
+	id="where"
+	name="where"
+	description="Create a guards block"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>where&#x9;&#xD;@${grd} ${predicate}&#x9;</template>
+
+<template
+	id="with"
+	name="with"
+	description="Create a witnesses block"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>with&#x9;&#xD;@${variable} ${predicate}&#x9;</template>
+
+<template
+	id="then"
+	name="then"
+	description="Create a action block"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>then&#x9;&#xD;@${act} ${assignment}&#x9;</template>
+
+<template
+	id="guard"
+	name="guard"
+	description="Create a new guard"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>@${grd} ${predicate}&#x9;</template>
+
+<template
+	id="assignment_becomes_equal"
+	name="assignment_equal"
+	description="Create a new 'becomes equal' assignment"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>@${act} ${var} := ${value}&#x9;</template>
+
+<template
+	id="assignment_becomes_such"
+	name="assignment_such"
+	description="Create a new 'becomes such that' assignment"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>@${act} ${var} :| ${value}&#x9;</template>
+
+<template
+	id="assignment_becomes_member"
+	name="assignment_member"
+	description="Create a new 'becomes member of' assignment"
+	context="org.eventb.texteditor.events"
+	enabled="true"
+	>@${act} ${var} :: ${value}&#x9;</template>
+
+
+<!--
+ ######## ANYWHERE context ######## 
+-->
+
+<template
+	id="equivalence1"
+	name="equivalence"
+	description="Equivalence: &#x21D4; ${P}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x21D4; ${P}</template>
+<template
+	id="equivalence2"
+	name="equivalence"
+	description="Equivalence: ${P1} &#x21D4; ${P2}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${P1} &#x21D4; ${P2}</template>
+
+<template
+	id="implication1"
+	name="implication"
+	description="Implication: &#x21D2; ${P}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x21D2; ${P}</template>
+<template
+	id="implication2"
+	name="implication"
+	description="Implication: ${P1} &#x21D2; ${P2}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${P1} &#x21D2; ${P2}</template>
+
+<template
+	id="and1"
+	name="and_predicate"
+	description="Logical and: &#x2227; ${P}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2227; ${P}</template>
+<template
+	id="and2"
+	name="and_predicate"
+	description="Logical and: ${P1} &#x2227; ${P2}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${P1} &#x2227; ${P2}</template>
+
+<template
+	id="or1"
+	name="or_predicate"
+	description="Logical or: ${P1} &#x2228; ${P2}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${P1} &#x2228; ${P2}</template>
+<template
+	id="or2"
+	name="or_predicate"
+	description="Logical or: &#x2228; ${P}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2228; ${P}</template>
+
+<template
+	id="not"
+	name="not"
+	description="Not: &#x00AC; ${P1}"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x00AC; ${P1}</template>
+
+<template
+	id="true_predicate"
+	name="true_predicate"
+	description="Predicate true: &#x22A4;"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x22A4;</template>
+
+<template
+	id="false_predicate"
+	name="false_predicate"
+	description="Predicate false: &#x22A5;"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x22A5;</template>
+
+<template
+	id="forall1"
+	name="forall"
+	description="For all predicate"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2200; ${var} &#x00B7; ${var}</template>
+<template
+	id="forall2"
+	name="forall"
+	description="For all predicate (with type)"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2200; ${var} &#x00B7; ${var} &#x2208; ${set}</template>
+<template
+	id="forall3"
+	name="forall"
+	description="For all predicate (with type and implication)"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2200; ${var} &#x00B7; ${var} &#x2208; ${set} &#x21D2; ${P}</template>
+
+<template
+	id="exists1"
+	name="exists"
+	description="Exists predicate"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2203; ${var} &#x00B7; ${var}</template>
+<template
+	id="exists2"
+	name="exists"
+	description="Exists predicate (with type)"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2203; ${var} &#x00B7; ${var} &#x2208; ${set}</template>
+<template
+	id="exists3"
+	name="exists"
+	description="Exists predicate (with type and implication)"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2203; ${var} &#x00B7; ${var} &#x2208; ${set} &#x21D2; ${P}</template>
+
+<template
+	id="relation1"
+	name="relation"
+	description="Relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2203; ${E}</template>
+<template
+	id="relation2"
+	name="relation"
+	description="Relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x2203; ${E2}</template>
+
+<template
+	id="total_relation1"
+	name="totalRelation"
+	description="Total relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#xE100; ${E}</template>
+<template
+	id="total_relation2"
+	name="totalRelation"
+	description="Total relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#xE100; ${E2}</template>
+
+<template
+	id="surjective_relation1"
+	name="surjectiveRelation"
+	description="Total relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#xE101; ${E}</template>
+<template
+	id="surjective_relation2"
+	name="surjectiveRelation"
+	description="Surjective relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#xE101; ${E2}</template>
+
+<template
+	id="surjective_total_relation1"
+	name="surjectiveTotalRelation"
+	description="Surjective total relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#xE102; ${E}</template>
+<template
+	id="surjective_total_relation2"
+	name="surjectiveTotalRelation"
+	description="Surjective total relation expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#xE102; ${E2}</template>
+
+<template
+	id="partial_function1"
+	name="partialFunction"
+	description="Partial function expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x21F8; ${E}</template>
+<template
+	id="partial_function2"
+	name="partialFunction"
+	description="Partial function expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x21F8; ${E2}</template>
+
+<template
+	id="total_function1"
+	name="totalFunction"
+	description="Total function expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2192; ${E}</template>
+<template
+	id="total_function2"
+	name="totalFunction"
+	description="Total function expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x2192; ${E2}</template>
+
+<template
+	id="partial_injection1"
+	name="partialInjection"
+	description="Partial injection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2914; ${E}</template>
+<template
+	id="partial_injection2"
+	name="partialInjection"
+	description="Partial injection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x2914; ${E2}</template>
+
+<template
+	id="total_injection1"
+	name="totalInjection"
+	description="Total injection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x21A3; ${E}</template>
+<template
+	id="total_injection2"
+	name="totalInjection"
+	description="Total injection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x21A3; ${E2}</template>
+
+<template
+	id="partial_surjection1"
+	name="partialSurjection"
+	description="Partial surjection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2900; ${E}</template>
+<template
+	id="partial_surjection2"
+	name="partialSurjection"
+	description="Partial surjection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x2900; ${E2}</template>
+
+<template
+	id="total_surjection1"
+	name="totalSurjection"
+	description="Total surjection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x21A0; ${E}</template>
+<template
+	id="total_surjection2"
+	name="totalSurjection"
+	description="Total surjection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x21A0; ${E2}</template>
+
+<template
+	id="bijection1"
+	name="bijection"
+	description="Bijection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>&#x2916; ${E}</template>
+<template
+	id="bijection2"
+	name="bijection"
+	description="Bijection expression"
+	context="org.eventb.texteditor.anywhere"
+	enabled="true"
+	>${E1} &#x2916; ${E2}</template>
+
+</templates>
\ No newline at end of file
diff --git a/org.eventb.texttools/.classpath b/org.eventb.texttools/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..e89ef6cd4e764ef85f4209624426134214bd8cb0
--- /dev/null
+++ b/org.eventb.texttools/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src_generated"/>
+	<classpathentry exported="true" kind="lib" path="lib/aspectjrt.jar"/>
+	<classpathentry kind="lib" path="lib/EventBParser.jar" sourcepath="/de.be4.eventb.structparser"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eventb.texttools/.project b/org.eventb.texttools/.project
new file mode 100644
index 0000000000000000000000000000000000000000..936ba24962a14bd8ff48500da35b74f50b4e1d99
--- /dev/null
+++ b/org.eventb.texttools/.project
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eventb.texttools</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eventb.texttools/.settings/org.eclipse.jdt.core.prefs b/org.eventb.texttools/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..0178f55888c2ce9819b5d9864de0938aa301633d
--- /dev/null
+++ b/org.eventb.texttools/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Sun May 10 11:23:16 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/org.eventb.texttools/META-INF/MANIFEST.MF b/org.eventb.texttools/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000000000000000000000000000000000..16453fd500efce05f63152cc768c25e26d70b854
--- /dev/null
+++ b/org.eventb.texttools/META-INF/MANIFEST.MF
@@ -0,0 +1,26 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Event-B EMF Texttools
+Bundle-SymbolicName: org.eventb.texttools;singleton:=true
+Bundle-Version: 1.1.2
+Bundle-Activator: org.eventb.texttools.TextToolsPlugin
+Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.5.0,3.6.0)",
+ org.eclipse.jface.text;bundle-version="[3.5.0,3.6.0)",
+ org.eventb.emf.formulas;bundle-version="[1.0.3,2.0.0)",
+ org.eventb.emf.persistence;bundle-version="[1.1.2,2.0.0)",
+ org.eventb.core.ast;bundle-version="[1.2.0,1.3.0)",
+ org.rodinp.core;bundle-version="[1.2.0,1.3.0)",
+ org.eclipse.emf.compare;bundle-version="[1.0.0,2.0.0)",
+ org.eclipse.emf.compare.diff;bundle-version="[1.0.0,2.0.0)",
+ org.eclipse.emf.compare.match;bundle-version="[1.0.0,2.0.0)"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: org.eventb.texttools,
+ org.eventb.texttools.formulas,
+ org.eventb.texttools.merge,
+ org.eventb.texttools.model.texttools,
+ org.eventb.texttools.prettyprint
+Bundle-ClassPath: lib/aspectjrt.jar,
+ lib/EventBParser.jar,
+ .
+Bundle-Vendor: Heinrich-Heine University Dusseldorf
diff --git a/org.eventb.texttools/build.properties b/org.eventb.texttools/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..2edb91ddd1a5e4288c5e0e3b412b6bfe9d3f7780
--- /dev/null
+++ b/org.eventb.texttools/build.properties
@@ -0,0 +1,10 @@
+source.. = src/,\
+           src_generated/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               lib/EventBParser.jar,\
+               lib/aspectjrt.jar
+src.includes = lib/EventBParser.jar,\
+               lib/aspectjrt.jar
diff --git a/org.eventb.texttools/model/texttools.ecore b/org.eventb.texttools/model/texttools.ecore
new file mode 100644
index 0000000000000000000000000000000000000000..4ab5f22fb76db89a06adb87d8a6aed061f5f91e6
--- /dev/null
+++ b/org.eventb.texttools/model/texttools.ecore
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0"
+    xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="texttools"
+    nsURI="http://emf.eventb.org/models/core/texttools" nsPrefix="texttools">
+  <eClassifiers xsi:type="ecore:EClass" name="TextRange">
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="offset" lowerBound="1"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt" transient="true"
+        defaultValueLiteral="0"/>
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="length" lowerBound="1"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt" transient="true"
+        defaultValueLiteral="0"/>
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="subTextRanges" lowerBound="1"
+        transient="true">
+      <eGenericType eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EMap">
+        <eTypeArguments eClassifier="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+        <eTypeArguments eClassifier="#//TextRange"/>
+      </eGenericType>
+    </eStructuralFeatures>
+  </eClassifiers>
+</ecore:EPackage>
diff --git a/org.eventb.texttools/model/texttools.genmodel b/org.eventb.texttools/model/texttools.genmodel
new file mode 100644
index 0000000000000000000000000000000000000000..f050e8f674bed78bf1c7de8ab1ead8ccf56e4a63
--- /dev/null
+++ b/org.eventb.texttools/model/texttools.genmodel
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<genmodel:GenModel xmi:version="2.0"
+    xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" modelDirectory="/org.eventb.texttools/src"
+    modelPluginID="org.eventb.texttools" modelName="Texttools" importerID="org.eclipse.emf.importer.ecore"
+    complianceLevel="5.0" copyrightFields="false">
+  <foreignModel>texttools.ecore</foreignModel>
+  <genPackages prefix="Texttools" basePackage="org.eventb.texttools.model" disposableProviderFactory="true"
+      ecorePackage="texttools.ecore#/">
+    <genClasses ecoreClass="texttools.ecore#//TextRange">
+      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute texttools.ecore#//TextRange/offset"/>
+      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute texttools.ecore#//TextRange/length"/>
+    </genClasses>
+  </genPackages>
+</genmodel:GenModel>
diff --git a/org.eventb.texttools/plugin.properties b/org.eventb.texttools/plugin.properties
new file mode 100644
index 0000000000000000000000000000000000000000..fafcf04c1cb1f28d64ab65d0225494e6e340ac6f
--- /dev/null
+++ b/org.eventb.texttools/plugin.properties
@@ -0,0 +1,8 @@
+
+# <copyright>
+# </copyright>
+#
+# $Id$
+
+pluginName = Event-B EMF Texttools
+providerName = Heinrich-Heine University Dusseldorf
diff --git a/org.eventb.texttools/plugin.xml b/org.eventb.texttools/plugin.xml
new file mode 100644
index 0000000000000000000000000000000000000000..13948a526b0994f470a45b011f783b32239bbcf1
--- /dev/null
+++ b/org.eventb.texttools/plugin.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+   <extension
+         point="org.rodinp.core.attributeTypes">
+      <attributeType
+            id="text_representation"
+            kind="string"
+            name="Text Representation">
+      </attributeType>
+      <attributeType
+            id="text_lastmodified"
+            kind="long"
+            name="Text LastModified">
+      </attributeType>
+   </extension>
+   <extension
+         point="org.eclipse.emf.compare.match.engine">
+      <matchengine
+            label="EventB Match Engine"
+            engineClass="org.eventb.texttools.merge.EventBMatchEngine"
+            fileExtension="bum,buc">
+      </matchengine>
+   </extension>
+   <extension
+         point="org.eclipse.emf.compare.diff.mergerprovider">
+      <mergerprovider
+            fileExtension="bum,buc"
+            mergerProviderClass="org.eventb.texttools.merge.MergerProvider"
+            priority="high">
+      </mergerprovider>
+   </extension>
+</plugin>
diff --git a/org.eventb.texttools/src/org/eventb/texttools/Constants.java b/org.eventb.texttools/src/org/eventb/texttools/Constants.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b483b7e4a3d33c578dd4a5b2c802e96b1cd09e1
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/Constants.java
@@ -0,0 +1,54 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools;
+
+public interface Constants {
+	public static final String MACHINE = "machine";
+	public static final String CONTEXT = "context";
+	public static final String REFINES = "refines";
+	public static final String SEES = "sees";
+	public static final String EXTENDS = "extends";
+	public static final String VARIABLES = "variables";
+	public static final String INVARIANTS = "invariants";
+	public static final String THEOREM = "theorem";
+	public static final String VARIANT = "variant";
+	public static final String EVENTS = "events";
+	public static final String EVENT = "event";
+	public static final String ORDINARY = "ordinary";
+	public static final String ANTICIPATED = "anticipated";
+	public static final String CONVERGENT = "convergent";
+	public static final String ANY = "any";
+	public static final String WHERE = "where";
+	public static final String WHEN = "when";
+	public static final String WITH = "with";
+	public static final String THEN = "then";
+	public static final String BEGIN = "begin";
+	public static final String AXIOMS = "axioms";
+	public static final String CONSTANTS = "constants";
+	public static final String SETS = "sets";
+	public static final String END = "end";
+
+	public static final String[] structural_keywords = { MACHINE, CONTEXT,
+			REFINES, SEES, EXTENDS, VARIABLES, INVARIANTS, THEOREM, VARIANT,
+			EVENTS, EVENT, ORDINARY, ANTICIPATED, CONVERGENT, ANY, WHERE,
+			WHEN, WITH, THEN, BEGIN, AXIOMS, CONSTANTS, SETS, END };
+
+	public static final String[] machine_keywords = { MACHINE, REFINES, SEES,
+			EXTENDS, VARIABLES, INVARIANTS, THEOREM, VARIANT, EVENTS, END };
+
+	public static final String[] event_keywords = { REFINES, EXTENDS, EVENT,
+			ORDINARY, ANTICIPATED, CONVERGENT, ANY, WHERE, WHEN, WITH, THEN,
+			BEGIN, END };
+
+	public static final String[] context_keywords = { CONTEXT, EXTENDS, AXIOMS,
+			CONSTANTS, SETS, END };
+
+	public static final String[] formula_keywords = { "BOOL", "FALSE", "TRUE",
+			"bool", "card", "dom", "finite", "id", "inter", "max", "min",
+			"mod", "pred", "prj1", "prj2", "ran", "succ", "union", "\u2115",
+			"\u2115\u0031", "\u2119", "\u2119\u0031", "\u2124" };
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/ParseException.java b/org.eventb.texttools/src/org/eventb/texttools/ParseException.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd7973a63c12fd94114b454ea3d426965a3ed09f
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/ParseException.java
@@ -0,0 +1,35 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools;
+
+@SuppressWarnings("serial")
+public class ParseException extends Exception {
+
+	private final int line;
+	private final int position;
+	private final int tokenLength;
+
+	public ParseException(final String message, final int line,
+			final int position, final int tokenLength) {
+		super(message);
+		this.line = line;
+		this.position = position;
+		this.tokenLength = tokenLength;
+	}
+
+	public int getLine() {
+		return line;
+	}
+
+	public int getPosition() {
+		return position;
+	}
+
+	public int getTokenLength() {
+		return tokenLength;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/Parser.java b/org.eventb.texttools/src/org/eventb/texttools/Parser.java
new file mode 100644
index 0000000000000000000000000000000000000000..0513888e2a0688d09cf5a2ee839d751522c9c5ca
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/Parser.java
@@ -0,0 +1,111 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools;
+
+import org.eclipse.jface.text.IDocument;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.texttools.internal.parsing.TransformationVisitor;
+
+import de.be4.eventb.core.parser.BException;
+import de.be4.eventb.core.parser.EventBLexerException;
+import de.be4.eventb.core.parser.EventBParseException;
+import de.be4.eventb.core.parser.EventBParser;
+import de.be4.eventb.core.parser.node.Start;
+import de.be4.eventb.core.parser.node.Token;
+import de.be4.eventb.core.parser.parser.ParserException;
+import de.hhu.stups.sablecc.patch.SourcePositions;
+import de.hhu.stups.sablecc.patch.SourcecodeRange;
+
+public class Parser {
+	private final EventBParser parser = new EventBParser();
+	private final TransformationVisitor transformer = new TransformationVisitor();
+
+	/**
+	 * Parses the content of the given {@link IDocument}.
+	 * 
+	 * @param <T>
+	 * @param document
+	 * @return
+	 * @throws ParseException
+	 *             If the contents cannot be parsed
+	 * @throws IllegalArgumentException
+	 *             If called with <code>null</code> as document parameter
+	 */
+	public <T extends EventBObject> T parse(final IDocument document)
+			throws ParseException {
+		if (document == null) {
+			throw new IllegalArgumentException(
+					"Parser may not be called without input document");
+		}
+
+		final String input = document.get();
+
+		try {
+			final Start rootNode = parser.parse(input, false);
+			return transformer.<T>transform(rootNode, document);
+		} catch (final BException e) {
+			final Exception cause = e.getCause();
+
+			if (cause instanceof ParserException) {
+				final ParserException ex = (ParserException) cause;
+				final Token token = ex.getToken();
+
+				throw new ParseException(
+						adjustMessage(ex.getLocalizedMessage()), token
+								.getLine() - 1, token.getPos() - 1, token
+								.getText().length());
+			}
+			if (cause instanceof EventBLexerException) {
+				final EventBLexerException ex = (EventBLexerException) cause;
+				final String lastText = ex.getLastText();
+
+				throw new ParseException(
+						adjustMessage(ex.getLocalizedMessage()), ex
+								.getLastLine() - 1, ex.getLastPos() - 1,
+						lastText.length());
+			}
+
+			if (cause instanceof EventBParseException) {
+				final EventBParseException ex = (EventBParseException) cause;
+				final SourcecodeRange range = ex.getRange();
+				final SourcePositions positions = parser.getSourcePositions();
+
+				if (range != null && positions != null) {
+					throw new ParseException(adjustMessage(ex
+							.getLocalizedMessage()), positions
+							.getBeginLine(range) - 1, positions
+							.getBeginColumn(range) - 1, positions
+							.getRangeString(range).length());
+				} else {
+					final Token token = ex.getToken();
+					if (token != null) {
+						throw new ParseException(adjustMessage(ex
+								.getLocalizedMessage()), token.getLine() - 1,
+								token.getPos() - 1, token.getText().length());
+					}
+				}
+			}
+
+			throw new ParseException(e.getLocalizedMessage(), 0, 0, 1);
+		}
+	}
+
+	private String adjustMessage(final String localizedMessage) {
+		final StringBuilder result = new StringBuilder(localizedMessage);
+
+		// remove position information if found
+		final int posEnd = result.indexOf("] ");
+		if (result.charAt(0) == '[' && posEnd > 0) {
+			result.delete(0, posEnd + 2);
+		}
+
+		// make sure first character is upercase
+		result.setCharAt(0, Character.toUpperCase(result.charAt(0)));
+
+		return result.toString().trim();
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/PersistenceHelper.java b/org.eventb.texttools/src/org/eventb/texttools/PersistenceHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1a16f87ee1196038dd4825cd3b111d19ae4b1b8
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/PersistenceHelper.java
@@ -0,0 +1,270 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.EMap;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eventb.emf.core.Attribute;
+import org.eventb.emf.core.AttributeType;
+import org.eventb.emf.core.CoreFactory;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.texttools.merge.ModelMerge;
+import org.eventb.texttools.prettyprint.PrettyPrinter;
+
+public class PersistenceHelper {
+
+	public static IResource getIResource(final Resource resource) {
+		final URI uri = resource.getURI();
+		if (uri.isPlatformResource()) {
+			return ResourcesPlugin.getWorkspace().getRoot().findMember(
+					uri.toPlatformString(true));
+		}
+
+		return null;
+	}
+
+	public static void saveText(final Resource resource,
+			final boolean overwrite, final IProgressMonitor monitor)
+			throws CoreException {
+		try {
+			resource.save(Collections.EMPTY_MAP);
+
+			/*
+			 * Try to set timestamp to the same as in the annotation. Setting on
+			 * both Resource and IResource to be save.
+			 */
+			final long textTimestamp = getTextTimestamp(resource);
+			resource.setTimeStamp(textTimestamp);
+			getIResource(resource).setLocalTimeStamp(textTimestamp);
+		} catch (final IOException e) {
+			throw new CoreException(new Status(IStatus.ERROR,
+					TextToolsPlugin.PLUGIN_ID, "Saving to RodinDB failed", e));
+		}
+	}
+
+	public static void addTextAnnotation(final Resource resource,
+			final String textRepresentation, final long timeStamp)
+			throws CoreException {
+		final EventBNamedCommentedComponentElement component = getComponent(resource);
+		if (component != null) {
+			addTextAnnotation(component, textRepresentation, timeStamp);
+		} else {
+			throw new CoreException(new Status(IStatus.ERROR,
+					TextToolsPlugin.PLUGIN_ID,
+					"Resource has no EventBComponent"));
+		}
+	}
+
+	public static void addTextAnnotation(final EventBElement element,
+			final String textRepresentation, final long timeStamp) {
+		final EMap<String, Attribute> attributes = element.getAttributes();
+
+		// update text representation
+		Attribute textAttribute = attributes
+				.get(TextToolsPlugin.TYPE_TEXTREPRESENTATION.getId());
+		if (textAttribute == null) {
+			textAttribute = CoreFactory.eINSTANCE.createAttribute();
+			textAttribute.setType(AttributeType.STRING);
+			attributes.put(TextToolsPlugin.TYPE_TEXTREPRESENTATION.getId(),
+					textAttribute);
+		}
+		textAttribute.setValue(textRepresentation);
+
+		// update timestamp
+		Attribute timeAttribute = attributes
+				.get(TextToolsPlugin.TYPE_LASTMODIFIED.getId());
+		if (timeAttribute == null) {
+			timeAttribute = CoreFactory.eINSTANCE.createAttribute();
+			timeAttribute.setType(AttributeType.LONG);
+			attributes.put(TextToolsPlugin.TYPE_LASTMODIFIED.getId(),
+					timeAttribute);
+		}
+		timeAttribute.setValue(timeStamp);
+	}
+
+	private static void mergeComponents(final EventBNamedCommentedComponentElement oldVersion,
+			final EventBNamedCommentedComponentElement newVersion, final IProgressMonitor monitor) {
+		try {
+			final ModelMerge merge = new ModelMerge(oldVersion, newVersion);
+			merge.applyChanges(monitor);
+		} catch (final InterruptedException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public static void mergeRootElement(final Resource resource,
+			final EventBNamedCommentedComponentElement newVersion, final IProgressMonitor monitor) {
+		final EventBNamedCommentedComponentElement component = getComponent(resource);
+		if (component != null) {
+			mergeComponents(component, newVersion, monitor);
+		} else {
+			resource.getContents().add(newVersion);
+		}
+	}
+
+	public static String loadText(final Resource resource,
+			final String linebreak) throws IOException {
+		// make sure resource is loaded
+		if (!resource.isLoaded()) {
+			resource.load(Collections.EMPTY_MAP);
+		}
+
+		// check if we have the most recent text representation
+		if (isTextUptodate(resource)) {
+			// we should find a text representation in the EMF
+			final String text = getTextAnnotation(resource);
+
+			if (text != null) {
+				return text;
+			}
+		}
+
+		/*
+		 * Reload resource to get latest changes. This is necessary if the model
+		 * was already loaded and external changes have occured meanwhile. Then
+		 * unloading+loading makes sure the EMF persistence loads those changes.
+		 */
+		resource.unload();
+		resource.load(Collections.EMPTY_MAP);
+
+		// pretty-print the machine/context
+		final EventBNamedCommentedComponentElement rootElement = getComponent(resource);
+		if (rootElement != null) {
+			return getPrettyPrint(rootElement, linebreak);
+		} else {
+			throw new IOException(
+					"Cannot find load Event-B component: No machine/context found");
+		}
+	}
+
+	public static String getTextAnnotation(final Resource resource) {
+		final EMap<String, Attribute> attributes = getAttributesMap(resource);
+		if (attributes != null) {
+			final Attribute attr = attributes
+					.get(TextToolsPlugin.TYPE_TEXTREPRESENTATION.getId());
+
+			if (attr != null) {
+				return (String) attr.getValue();
+			}
+		}
+
+		return null;
+	}
+
+	public static EClass getComponentType(final Resource resource) {
+		final EventBNamedCommentedComponentElement component = getComponent(resource);
+
+		if (component != null) {
+			return component.eClass();
+		}
+
+		return null;
+	}
+
+	private static EventBNamedCommentedComponentElement getComponent(final Resource resource) {
+		final EList<EObject> contents = resource.getContents();
+		if (contents.size() > 0 && contents.get(0) instanceof EventBNamedCommentedComponentElement) {
+			return (EventBNamedCommentedComponentElement) contents.get(0);
+		}
+
+		return null;
+	}
+
+	/**
+	 * Extracts the timestamp of the latest saved text representation from the
+	 * EMF and returns it.
+	 *
+	 * @param resource
+	 * @return timestamp or <code>-1</code> if none is found
+	 */
+	private static long getTextTimestamp(final Resource resource) {
+		final EMap<String, Attribute> attributes = getAttributesMap(resource);
+		if (attributes != null) {
+			final Attribute attr = attributes
+					.get(TextToolsPlugin.TYPE_LASTMODIFIED.getId());
+			return attr != null ? (Long) attr.getValue() : -1;
+		}
+
+		return -1;
+	}
+
+	private static EMap<String, Attribute> getAttributesMap(
+			final Resource resource) {
+		final EList<EObject> contents = resource.getContents();
+		if (contents.size() > 0) {
+			final EObject object = contents.get(0);
+			if (object instanceof EventBNamedCommentedComponentElement) {
+				final EventBNamedCommentedComponentElement component = (EventBNamedCommentedComponentElement) object;
+				return component.getAttributes();
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Checks if the text representation saved in the EMF is up-to-date. The
+	 * timestamps in the EMF and of the underlying file are compared for this
+	 * decision.
+	 *
+	 * @param resource
+	 * @return <code>true</code> if there was no external change and the text
+	 *         representation is still up-to-date
+	 */
+	private static boolean isTextUptodate(final Resource resource) {
+		final long textTimestamp = getTextTimestamp(resource);
+
+		try {
+			final IResource file = getIResource(resource);
+			// refresh to get latest timestamp
+			file.refreshLocal(IResource.DEPTH_ONE, null);
+			final long resourceTimestamp = file.getLocalTimeStamp();
+
+			// easy case: text is newer than resource
+			if (textTimestamp >= resourceTimestamp) {
+				return true;
+			}
+
+			final long diff = resourceTimestamp - textTimestamp;
+
+			// tolerate 50ms offset (time to save file)
+			// TODO this is ugly!!!
+			if (diff < 50) {
+				return true;
+			}
+		} catch (final CoreException e) {
+			TextToolsPlugin.getDefault().getLog().log(
+					new Status(IStatus.ERROR, TextToolsPlugin.PLUGIN_ID,
+							"Error checking file timestamps", e));
+		}
+
+		return false;
+	}
+
+	private static String getPrettyPrint(final EventBElement rootElement,
+			final String linebreak) {
+		final StringBuilder buffer = new StringBuilder();
+		new PrettyPrinter(buffer, linebreak, null).prettyPrint(rootElement);
+
+		return buffer.toString();
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/ResourceManager.java b/org.eventb.texttools/src/org/eventb/texttools/ResourceManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..4c3736bfc990737f3e64d2b51bc5ce201bb92a4a
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/ResourceManager.java
@@ -0,0 +1,55 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.emf.common.command.BasicCommandStack;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
+import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
+import org.eventb.emf.core.context.provider.ContextItemProviderAdapterFactory;
+import org.eventb.emf.core.machine.provider.MachineItemProviderAdapterFactory;
+import org.eventb.emf.core.provider.CoreItemProviderAdapterFactory;
+
+public class ResourceManager {
+	private final Map<IProject, AdapterFactoryEditingDomain> projectEditingDomains = new HashMap<IProject, AdapterFactoryEditingDomain>();
+
+	public AdapterFactoryEditingDomain getEditingDomain(final IProject project) {
+		if (!projectEditingDomains.containsKey(project)) {
+			final AdapterFactoryEditingDomain editingDomain = initializeEditingDomain();
+			projectEditingDomains.put(project, editingDomain);
+		}
+
+		return projectEditingDomains.get(project);
+	}
+
+	private AdapterFactoryEditingDomain initializeEditingDomain() {
+		// Create an adapter factory that yields item providers.
+		final ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(
+				ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
+
+		adapterFactory
+				.addAdapterFactory(new ResourceItemProviderAdapterFactory());
+		adapterFactory.addAdapterFactory(new CoreItemProviderAdapterFactory());
+		adapterFactory
+				.addAdapterFactory(new MachineItemProviderAdapterFactory());
+		adapterFactory
+				.addAdapterFactory(new ContextItemProviderAdapterFactory());
+		adapterFactory
+				.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
+
+		final BasicCommandStack commandStack = new BasicCommandStack();
+
+		return new AdapterFactoryEditingDomain(adapterFactory, commandStack,
+				new HashMap<Resource, Boolean>());
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/TextPositionUtil.java b/org.eventb.texttools/src/org/eventb/texttools/TextPositionUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..31849504a9bbba36ded1960f3b99b1887539006c
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/TextPositionUtil.java
@@ -0,0 +1,235 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EAnnotation;
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.texttools.model.texttools.TextRange;
+import org.eventb.texttools.model.texttools.TexttoolsFactory;
+
+public class TextPositionUtil {
+	/**
+	 * Source key for the {@link EAnnotation} containing {@link TextRange}.
+	 */
+	public static final String ANNOTATION_TEXTRANGE = "http://emf.eventb.org/models/core/texttools/TextRange";
+
+	/**
+	 * <p>
+	 * Extracts a {@link TextRange} object from the annotations of the given
+	 * {@link EModelElement}. The <code>contents</code> attribute of the
+	 * annotation is search for an object of type {@link TextRange} and the
+	 * first one found is returned.
+	 * </p>
+	 * <p>
+	 * If no matching object can be found <code>null</code> is returned.
+	 * </p>
+	 * 
+	 * @param annotation
+	 * @return
+	 */
+	public static TextRange getTextRange(final EModelElement element) {
+		if (element != null) {
+			final EAnnotation annotation = element
+					.getEAnnotation(ANNOTATION_TEXTRANGE);
+
+			if (annotation != null) {
+				final EList<EObject> contents = annotation.getContents();
+
+				for (final EObject object : contents) {
+					if (object instanceof TextRange) {
+						return (TextRange) object;
+					}
+				}
+			}
+		}
+
+		return null;
+	}
+
+	public static void annotatePosition(final EModelElement element,
+			final TextRange range) {
+		if (range != null) {
+			final EAnnotation annotation = EcoreFactory.eINSTANCE
+					.createEAnnotation();
+			annotation.setEModelElement(element);
+			annotation.setSource(TextPositionUtil.ANNOTATION_TEXTRANGE);
+			annotation.getContents().add(range);
+
+			element.getEAnnotations().add(annotation);
+		}
+	}
+
+	public static void annotatePosition(final EModelElement element,
+			final int startPos, final int length) {
+		final TextRange range = TexttoolsFactory.eINSTANCE.createTextRange();
+		range.setOffset(startPos);
+		range.setLength(length);
+
+		annotatePosition(element, range);
+	}
+
+	/**
+	 * <p>
+	 * Gets the position annotation of the given {@link EModelElement} and if
+	 * it's not <code>null</code> the given {@link TextRange} is attached to it
+	 * under the given key.
+	 * </p>
+	 * <p>
+	 * This can be used to store position information about subelements, i.e.,
+	 * strings. First annotate the EventB element normally. Then create a
+	 * {@link TextRange} object for each string for which you want to store the
+	 * positions. Then pass the parent EventB element, the new {@link TextRange}
+	 * to this method and an appropriate {@link String} as key to this method.
+	 * </p>
+	 * 
+	 * @see
+	 * @param emfElement
+	 *            Parent element which already has a position annotation to
+	 *            which the given annotation is to be attached.
+	 * @param range
+	 *            The annotation which will be attached to the parent's position
+	 *            annotation.
+	 * @param key
+	 *            A key to store the position under. It will be needed to
+	 *            retrieve the position later. So it's helpful to use the string
+	 *            itself.
+	 */
+	public static void addInternalPosition(final EModelElement emfElement,
+			final String key, final TextRange range) {
+		Assert.isNotNull(emfElement);
+		Assert.isNotNull(range);
+		Assert.isNotNull(key);
+
+		final TextRange parentRange = getTextRange(emfElement);
+
+		if (parentRange != null) {
+			getSubRangeMap(parentRange).put(key, range);
+		}
+	}
+
+	/**
+	 * Replace the {@link TextRange} that is associated with the given
+	 * <code>oldKey</code>. Several cases are possible:
+	 * <ul>
+	 * <li><code>newKey == null</code>: The old {@link TextRange} is just
+	 * removed.</li>
+	 * <li><code>newKey == oldKey</code>: The {@link TextRange} is simply
+	 * replaced.</li>
+	 * <li><code>newKey != null && newKey != oldKey</code>: The
+	 * {@link TextRange} under key <code>oldKey</code> is removed and the
+	 * <code>newRange</code> is added under <code>newKey</code>.</li>
+	 * </ul>
+	 * 
+	 * @param emfElement
+	 * @param oldKey
+	 * @param newKey
+	 * @param newRange
+	 */
+	public static void replaceInternalPosition(final EModelElement emfElement,
+			final String oldKey, final String newKey, final TextRange newRange) {
+		Assert.isNotNull(emfElement);
+		Assert.isNotNull(oldKey);
+
+		final TextRange parentRange = getTextRange(emfElement);
+
+		if (parentRange != null) {
+			final Map<String, TextRange> subRangeMap = getSubRangeMap(parentRange);
+			subRangeMap.remove(oldKey);
+
+			if (newKey != null && newRange != null) {
+				subRangeMap.put(newKey, newRange);
+			}
+		}
+	}
+
+	/**
+	 * Extracts a {@link TextRange} for a substring from the given
+	 * {@link EModelElement}'s annotations. The given key is used to find the
+	 * correct {@link TextRange} for the substring.
+	 * 
+	 * @see #addInternalPosition(EModelElement, String, TextRange)
+	 * @param emfElement
+	 *            Parent element with a position annotation.
+	 * @param key
+	 *            Key which has been used to store the position information.
+	 * @return The relevant {@link TextRange} if available. Returns
+	 *         <code>null</code> if the {@link EModelElement} has no position
+	 *         annotation or if this annotation didn't contain matching key.
+	 */
+	public static TextRange getInternalPosition(final EModelElement emfElement,
+			final String key) {
+		Assert.isNotNull(emfElement);
+		Assert.isNotNull(key);
+
+		final TextRange parentRange = getTextRange(emfElement);
+
+		if (parentRange != null) {
+			return getSubRangeMap(parentRange).get(key);
+		}
+
+		return null;
+	}
+
+	private static Map<String, TextRange> getSubRangeMap(
+			final TextRange parentRange) {
+		Map<String, TextRange> result = parentRange.getSubTextRanges();
+
+		if (result == null) {
+			result = new HashMap<String, TextRange>();
+			parentRange.setSubTextRanges(result);
+		}
+
+		return result;
+	}
+
+	/**
+	 * Creates a new {@link TextRange} instance initialised with the value of
+	 * the given {@link EventBObject}. So the returned object may be changed
+	 * without changing the the position of the given emf object.
+	 * 
+	 * @param element
+	 *            {@link EventBObject} with original position
+	 * @return New {@link TextRange} instance or <code>null</code> if no
+	 *         original text position could be found.
+	 */
+	public static TextRange createTextRange(final EventBObject element) {
+		final TextRange origRange = getTextRange(element);
+
+		if (origRange != null) {
+			final TextRange result = TexttoolsFactory.eINSTANCE
+					.createTextRange();
+			result.setLength(origRange.getLength());
+			result.setOffset(origRange.getOffset());
+			return result;
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Pushes the offset back by the given length and corrects the total length
+	 * of this {@link TextRange}.
+	 * 
+	 * @param range
+	 * @param length
+	 */
+	public static void correctStartOffset(final TextRange range,
+			final int length) {
+		if (range != null) {
+			range.setOffset(range.getOffset() + length + 1);
+			range.setLength(range.getLength() - length - 1);
+		}
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/TextToolsPlugin.java b/org.eventb.texttools/src/org/eventb/texttools/TextToolsPlugin.java
new file mode 100644
index 0000000000000000000000000000000000000000..93e59682ad8d5882f2e8639c61dfcb4f28f1f018
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/TextToolsPlugin.java
@@ -0,0 +1,75 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools;
+
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.BundleContext;
+import org.rodinp.core.IAttributeType;
+import org.rodinp.core.RodinCore;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class TextToolsPlugin extends Plugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.eventb.texttools";
+
+	public static final IAttributeType.String TYPE_TEXTREPRESENTATION = RodinCore
+			.getStringAttrType(PLUGIN_ID + ".text_representation");
+	public static final IAttributeType.Long TYPE_LASTMODIFIED = RodinCore
+			.getLongAttrType(PLUGIN_ID + ".text_lastmodified");
+
+	// The shared instance
+	private static TextToolsPlugin plugin;
+
+	private ResourceManager resourceManager;
+
+	/**
+	 * The constructor
+	 */
+	public TextToolsPlugin() {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext)
+	 */
+	@Override
+	public void start(final BundleContext context) throws Exception {
+		super.start(context);
+		plugin = this;
+		resourceManager = new ResourceManager();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
+	 */
+	@Override
+	public void stop(final BundleContext context) throws Exception {
+		plugin = null;
+		super.stop(context);
+	}
+
+	/**
+	 * Returns the shared instance
+	 * 
+	 * @return the shared instance
+	 */
+	public static TextToolsPlugin getDefault() {
+		return plugin;
+	}
+
+	public ResourceManager getResourceManager() {
+		return resourceManager;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/formulas/ContextResolveSwitch.java b/org.eventb.texttools/src/org/eventb/texttools/formulas/ContextResolveSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..7756c80bdaf2fe365e28611d92edbac57748654d
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/formulas/ContextResolveSwitch.java
@@ -0,0 +1,50 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.formulas;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.emf.ecore.EModelElement;
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.util.ContextSwitch;
+
+public class ContextResolveSwitch extends ContextSwitch<Boolean> {
+
+	private final List<FormulaParseException> exceptions = new ArrayList<FormulaParseException>();
+
+	@Override
+	public Boolean caseEModelElement(final EModelElement object) {
+		return true;
+	}
+
+	@Override
+	public Boolean caseEventBObject(final EventBObject object) {
+		return true;
+	}
+
+	@Override
+	public Boolean caseEventBNamedCommentedPredicateElement(final EventBNamedCommentedPredicateElement object) {
+		try {
+			FormulaResolver.resolve(object);
+		} catch (final FormulaParseException e) {
+			handleError(e);
+		}
+
+		// no need to traverse any children
+		return false;
+	}
+
+	private void handleError(final FormulaParseException e) {
+		exceptions.add(e);
+	}
+
+	public List<FormulaParseException> getExceptions() {
+		return exceptions;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/formulas/ExtensionHelper.java b/org.eventb.texttools/src/org/eventb/texttools/formulas/ExtensionHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b53b41c4c09c8123972e4e24f36ea0208fa52d3
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/formulas/ExtensionHelper.java
@@ -0,0 +1,39 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.formulas;
+
+import org.eclipse.emf.common.util.EList;
+import org.eventb.emf.core.AbstractExtension;
+import org.eventb.emf.formulas.BFormula;
+
+public class ExtensionHelper {
+
+	/**
+	 * Searches for a {@link BFormula} extension in the list and returns the
+	 * first one found as the expected type <code>T</code>. Misuse will cause a
+	 * {@link ClassCastException} to be thrown. If no matching extension element
+	 * is found <code>null</code> is returned.
+	 * 
+	 * @param <T>
+	 * @param extensions
+	 * @return Formula casted to type <code>T</code> or <code>null</code> if no
+	 *         matching one is found.
+	 * @throws ClassCastException
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T extends BFormula> T getFormula(
+			final EList<AbstractExtension> extensions)
+			throws ClassCastException {
+		for (final AbstractExtension extension : extensions) {
+			if (extension instanceof BFormula) {
+				return (T) extension;
+			}
+		}
+
+		return null;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/formulas/FormulaParseException.java b/org.eventb.texttools/src/org/eventb/texttools/formulas/FormulaParseException.java
new file mode 100644
index 0000000000000000000000000000000000000000..73ed827c270ce61c4613ea4604a036918d53e47e
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/formulas/FormulaParseException.java
@@ -0,0 +1,80 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.formulas;
+
+import java.util.List;
+
+import org.eventb.core.ast.ASTProblem;
+import org.eventb.core.ast.SourceLocation;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBExpression;
+
+@SuppressWarnings("serial")
+public class FormulaParseException extends Exception {
+
+	private final EventBElement emfExpression;
+
+	private final List<ASTProblem> astProblems;
+
+	private final String formula;
+
+	public FormulaParseException(final EventBElement emfExpr,
+			final String formula, final List<ASTProblem> problems) {
+		emfExpression = emfExpr;
+		this.formula = formula;
+		astProblems = problems;
+	}
+
+	/**
+	 * The {@link EventBExpression} which caused the problem. The Rodin formula
+	 * which was parsed can be found in {@link EventBExpression#getExpression()}
+	 * .
+	 * 
+	 * @return
+	 */
+	public EventBElement getEmfObject() {
+		return emfExpression;
+	}
+
+	/**
+	 * The {@link ASTProblem}s that occured during parsing the formula (see
+	 * {@link #getEmfObject()} -> {@link EventBExpression#getExpression()}.
+	 * 
+	 * @return
+	 */
+	public List<ASTProblem> getAstProblems() {
+		return astProblems;
+	}
+
+	public String getFormula() {
+		return formula;
+	}
+
+	@Override
+	public String getLocalizedMessage() {
+		final StringBuilder buffer = new StringBuilder();
+		buffer.append("Parse problems in formula '");
+		buffer.append(getFormula());
+		buffer.append("': [");
+		
+		List<ASTProblem> problems = getAstProblems();
+		for (int i = 0; i < problems.size(); i++) {
+			ASTProblem problem = problems.get(i);
+			
+			buffer.append(" (");
+			SourceLocation location = problem.getSourceLocation();
+			buffer.append(location.getStart() + "-" +location.getEnd());
+			buffer.append(") ");
+			buffer.append(String
+					.format(problem.getMessage().toString(), problem.getArgs()));
+		}
+
+		buffer.append(" ]");
+		
+		return buffer.toString();
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/formulas/FormulaResolver.java b/org.eventb.texttools/src/org/eventb/texttools/formulas/FormulaResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..1218583c337505da76778812be6da6c4335d69fc
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/formulas/FormulaResolver.java
@@ -0,0 +1,136 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.formulas;
+
+import java.util.List;
+
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eventb.core.ast.ASTProblem;
+import org.eventb.core.ast.FormulaFactory;
+import org.eventb.core.ast.IParseResult;
+import org.eventb.core.ast.LanguageVersion;
+import org.eventb.emf.core.EventBCommentedExpressionElement;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.formulas.BFormula;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.model.texttools.TextRange;
+
+public class FormulaResolver {
+
+	private enum FormulaType {
+		Expression, Predicate, Assignment
+	};
+
+	private static FormulaFactory formulaFactory = FormulaFactory.getDefault();
+
+	public static List<FormulaParseException> resolveAllFormulas(
+			final EventBNamedCommentedComponentElement astRoot) {
+		// traverse tree using an iterator
+		final TreeIterator<EObject> iterator = EcoreUtil.getAllContents(
+				astRoot, false);
+		List<FormulaParseException> exceptions = null;
+
+		if (astRoot instanceof Machine) {
+			final MachineResolveSwitch switcher = new MachineResolveSwitch();
+			while (iterator.hasNext()) {
+				// visit node
+				final EObject next = iterator.next();
+				final Boolean visitChildren = switcher.doSwitch(next);
+
+				if (visitChildren != null ? !visitChildren : true) {
+					iterator.prune();
+				}
+			}
+			exceptions = switcher.getExceptions();
+		} else if (astRoot instanceof Context) {
+			final ContextResolveSwitch switcher = new ContextResolveSwitch();
+			while (iterator.hasNext()) {
+				// visit node
+				final EObject next = iterator.next();
+				final Boolean visitChildren = switcher.doSwitch(next);
+
+				if (visitChildren != null ? !visitChildren : true) {
+					iterator.prune();
+				}
+			}
+			exceptions = switcher.getExceptions();
+		}
+
+		return exceptions;
+	}
+
+	public static void resolve(final EventBCommentedExpressionElement emfExpr)
+			throws FormulaParseException {
+		final String expression = emfExpr.getExpression();
+		resolve(emfExpr, expression, formulaFactory.parseExpression(expression,
+				LanguageVersion.V2, emfExpr), FormulaType.Expression);
+	}
+
+	public static void resolve(final EventBNamedCommentedPredicateElement emfPredicate)
+			throws FormulaParseException {
+		final String predicate = emfPredicate.getPredicate();
+		resolve(emfPredicate, predicate, formulaFactory.parsePredicate(
+				predicate, LanguageVersion.V2, emfPredicate),
+				FormulaType.Predicate);
+	}
+
+	public static void resolve(final Action emfAction)
+			throws FormulaParseException {
+		final String action = emfAction.getAction();
+		resolve(emfAction, action, formulaFactory.parseAssignment(action,
+				LanguageVersion.V2, emfAction), FormulaType.Assignment);
+	}
+
+	private static void resolve(final EventBElement emfExpr,
+			final String content, final IParseResult parseResult,
+			final FormulaType type) throws FormulaParseException {
+		// parsing comes first
+		final List<ASTProblem> problems = parseResult.getProblems();
+
+		// no need to continue if any problems occured
+		if (problems.size() > 0) {
+			throw new FormulaParseException(emfExpr, content, problems);
+		}
+
+		final ResolveVisitor visitor = new ResolveVisitor();
+		BFormula formula = null;
+		final int offset = getOffset(emfExpr, content);
+
+		switch (type) {
+		case Expression:
+			formula = visitor
+					.convert(parseResult.getParsedExpression(), offset);
+			break;
+		case Predicate:
+			formula = visitor.convert(parseResult.getParsedPredicate(), offset);
+			break;
+		case Assignment:
+			formula = visitor
+					.convert(parseResult.getParsedAssignment(), offset);
+			break;
+
+		default:
+			break;
+		}
+
+		emfExpr.getExtensions().add(formula);
+	}
+
+	private static int getOffset(final EventBElement emfObject,
+			final String content) {
+		final TextRange range = TextPositionUtil.getInternalPosition(emfObject,
+				content);
+		return range != null ? range.getOffset() : 0;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/formulas/MachineResolveSwitch.java b/org.eventb.texttools/src/org/eventb/texttools/formulas/MachineResolveSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce4ff576bd9b7c0ff84a1259a418f22fff3d329b
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/formulas/MachineResolveSwitch.java
@@ -0,0 +1,76 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.formulas;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.emf.ecore.EModelElement;
+import org.eventb.emf.core.EventBCommentedExpressionElement;
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.util.MachineSwitch;
+
+public class MachineResolveSwitch extends MachineSwitch<Boolean> {
+
+	private final List<FormulaParseException> exceptions = new ArrayList<FormulaParseException>();
+
+	@Override
+	public Boolean caseEModelElement(final EModelElement object) {
+		return true;
+	}
+
+	@Override
+	public Boolean caseEventBObject(final EventBObject object) {
+		return true;
+	}
+
+	@Override
+	public Boolean caseEventBNamedCommentedPredicateElement(final EventBNamedCommentedPredicateElement object) {
+		try {
+			FormulaResolver.resolve(object);
+		} catch (final FormulaParseException e) {
+			handleError(e);
+		}
+
+		// no need to traverse any children
+		return false;
+	}
+
+	@Override
+	public Boolean caseEventBCommentedExpressionElement(final EventBCommentedExpressionElement object) {
+		try {
+			FormulaResolver.resolve(object);
+		} catch (final FormulaParseException e) {
+			handleError(e);
+		}
+
+		// no need to traverse any children
+		return false;
+	}
+
+	@Override
+	public Boolean caseAction(final Action object) {
+		try {
+			FormulaResolver.resolve(object);
+		} catch (final FormulaParseException e) {
+			handleError(e);
+		}
+
+		// no need to traverse any children
+		return false;
+	}
+
+	private void handleError(final FormulaParseException e) {
+		exceptions.add(e);
+	}
+
+	public List<FormulaParseException> getExceptions() {
+		return exceptions;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/formulas/ResolveVisitor.java b/org.eventb.texttools/src/org/eventb/texttools/formulas/ResolveVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..9555e5006efd1316ebf0f3686296f42f5736d4d9
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/formulas/ResolveVisitor.java
@@ -0,0 +1,726 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.formulas;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eventb.core.ast.Assignment;
+import org.eventb.core.ast.AssociativeExpression;
+import org.eventb.core.ast.AssociativePredicate;
+import org.eventb.core.ast.AtomicExpression;
+import org.eventb.core.ast.BecomesEqualTo;
+import org.eventb.core.ast.BecomesMemberOf;
+import org.eventb.core.ast.BecomesSuchThat;
+import org.eventb.core.ast.BinaryExpression;
+import org.eventb.core.ast.BinaryPredicate;
+import org.eventb.core.ast.BoolExpression;
+import org.eventb.core.ast.BoundIdentDecl;
+import org.eventb.core.ast.BoundIdentifier;
+import org.eventb.core.ast.Expression;
+import org.eventb.core.ast.Formula;
+import org.eventb.core.ast.FreeIdentifier;
+import org.eventb.core.ast.ISimpleVisitor;
+import org.eventb.core.ast.IntegerLiteral;
+import org.eventb.core.ast.LiteralPredicate;
+import org.eventb.core.ast.MultiplePredicate;
+import org.eventb.core.ast.Predicate;
+import org.eventb.core.ast.QuantifiedExpression;
+import org.eventb.core.ast.QuantifiedPredicate;
+import org.eventb.core.ast.RelationalPredicate;
+import org.eventb.core.ast.SetExtension;
+import org.eventb.core.ast.SimplePredicate;
+import org.eventb.core.ast.SourceLocation;
+import org.eventb.core.ast.UnaryExpression;
+import org.eventb.core.ast.UnaryPredicate;
+import org.eventb.emf.formulas.BAssignmentResolved;
+import org.eventb.emf.formulas.BExpressionResolved;
+import org.eventb.emf.formulas.BFormula;
+import org.eventb.emf.formulas.BPredicateResolved;
+import org.eventb.emf.formulas.BecomesEqualToAssignment;
+import org.eventb.emf.formulas.BecomesMemberOfAssignment;
+import org.eventb.emf.formulas.BecomesSuchThatAssignment;
+import org.eventb.emf.formulas.BinaryOperator;
+import org.eventb.emf.formulas.BoundIdentifierExpression;
+import org.eventb.emf.formulas.Constant;
+import org.eventb.emf.formulas.ExistPredicate;
+import org.eventb.emf.formulas.ForallPredicate;
+import org.eventb.emf.formulas.FormulasFactory;
+import org.eventb.emf.formulas.FormulasPackage;
+import org.eventb.emf.formulas.IdentifierExpression;
+import org.eventb.emf.formulas.IntegerLiteralExpression;
+import org.eventb.emf.formulas.MultiOperand;
+import org.eventb.emf.formulas.QuantifiedIntersectionExpression1;
+import org.eventb.emf.formulas.QuantifiedIntersectionExpression2;
+import org.eventb.emf.formulas.QuantifiedUnionExpression1;
+import org.eventb.emf.formulas.QuantifiedUnionExpression2;
+import org.eventb.emf.formulas.SetComprehensionExpression1;
+import org.eventb.emf.formulas.SetComprehensionExpression2;
+import org.eventb.emf.formulas.UnaryOperator;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.TextToolsPlugin;
+
+public class ResolveVisitor implements ISimpleVisitor {
+	private static final Map<Integer, EClass> idToEClass = new HashMap<Integer, EClass>();
+
+	@SuppressWarnings("unchecked")
+	private Map<BFormula, Formula> emfToRodinElements;
+
+	private final Stack<BFormula> stack = new Stack<BFormula>();
+	private final Stack<List<String>> boundIdDeclStack = new Stack<List<String>>();
+
+	private int textOffset;
+
+	static {
+		// Constants:
+		// INTEGER, NATURAL, NATURAL1, BOOL, TRUE, BTRUE, FALSE, BFALSE,
+		// EMPTYSET
+		idToEClass.put(Formula.INTEGER, FormulasPackage.eINSTANCE.getINT());
+		idToEClass.put(Formula.NATURAL, FormulasPackage.eINSTANCE.getNAT());
+		idToEClass.put(Formula.NATURAL1, FormulasPackage.eINSTANCE.getNAT1());
+		idToEClass.put(Formula.BOOL, FormulasPackage.eINSTANCE.getBOOL());
+		idToEClass.put(Formula.TRUE, FormulasPackage.eINSTANCE.getTRUE());
+		idToEClass.put(Formula.BTRUE, FormulasPackage.eINSTANCE.getTRUTH());
+		idToEClass.put(Formula.FALSE, FormulasPackage.eINSTANCE.getFALSE());
+		idToEClass.put(Formula.BFALSE, FormulasPackage.eINSTANCE.getFALSITY());
+		idToEClass.put(Formula.EMPTYSET, FormulasPackage.eINSTANCE
+				.getEMPTYSET());
+		idToEClass.put(Formula.KPRED, FormulasPackage.eINSTANCE
+				.getPredExpression());
+		idToEClass.put(Formula.KSUCC, FormulasPackage.eINSTANCE
+				.getSuccExpression());
+
+		// Unary operators:
+		// KCARD, POW, POW1, KUNION, KINTER, KDOM, KRAN, KPRJ1, KPRJ2, KID,
+		// KMIN, KMAX, CONVERSE, UNMINUS
+		// NOT, KFINITE, KBOOL
+		idToEClass.put(Formula.KCARD, FormulasPackage.eINSTANCE
+				.getCardExpression());
+		idToEClass.put(Formula.POW, FormulasPackage.eINSTANCE
+				.getPowExpression());
+		idToEClass.put(Formula.POW1, FormulasPackage.eINSTANCE
+				.getPow1Expression());
+		idToEClass.put(Formula.KUNION, FormulasPackage.eINSTANCE
+				.getKUnionExpression());
+		idToEClass.put(Formula.KINTER, FormulasPackage.eINSTANCE
+				.getKIntersectionExpression());
+		idToEClass.put(Formula.KDOM, FormulasPackage.eINSTANCE
+				.getDomainExpression());
+		idToEClass.put(Formula.KRAN, FormulasPackage.eINSTANCE
+				.getRangeExpression());
+		idToEClass.put(Formula.KPRJ1, FormulasPackage.eINSTANCE
+				.getPrj1Expression());
+		idToEClass.put(Formula.KPRJ1_GEN, FormulasPackage.eINSTANCE
+				.getPrj1GenExpression());
+		idToEClass.put(Formula.KPRJ2, FormulasPackage.eINSTANCE
+				.getPrj2Expression());
+		idToEClass.put(Formula.KPRJ2_GEN, FormulasPackage.eINSTANCE
+				.getPrj2GenExpression());
+		idToEClass.put(Formula.KID, FormulasPackage.eINSTANCE
+				.getIdentityExpression());
+		idToEClass.put(Formula.KID_GEN, FormulasPackage.eINSTANCE
+				.getIdentityGenExpression());
+		idToEClass.put(Formula.KMIN, FormulasPackage.eINSTANCE
+				.getMinExpression());
+		idToEClass.put(Formula.KMAX, FormulasPackage.eINSTANCE
+				.getMaxExpression());
+		idToEClass.put(Formula.CONVERSE, FormulasPackage.eINSTANCE
+				.getInverseExpression());
+		idToEClass.put(Formula.UNMINUS, FormulasPackage.eINSTANCE
+				.getUnaryMinusExpression());
+		idToEClass
+				.put(Formula.NOT, FormulasPackage.eINSTANCE.getNotPredicate());
+		idToEClass.put(Formula.KFINITE, FormulasPackage.eINSTANCE
+				.getFinitePredicate());
+		idToEClass.put(Formula.KBOOL, FormulasPackage.eINSTANCE
+				.getBoolExpression());
+
+		// Binary operators: EQUAL, NOTEQUAL, LT, LE, GT, GE, IN, NOTIN, SUBSET,
+		// NOTSUBSET, SUBSETEQ, NOTSUBSETEQ, MAPSTO, REL, TREL, SREL, STREL,
+		// PFUN, TFUN, PINJ, TINJ, PSUR, TSUR, TBIJ, SETMINUS, CPROD, DPROD,
+		// PPROD, DOMRES, DOMSUB, RANRES, RANSUB, UPTO, MINUS, DIV, MOD, EXPN,
+		// FUNIMAGE, RELIMAGE, LIMP, LEQV
+		idToEClass.put(Formula.EQUAL, FormulasPackage.eINSTANCE
+				.getEqualPredicate());
+		idToEClass.put(Formula.NOTEQUAL, FormulasPackage.eINSTANCE
+				.getNotEqualPredicate());
+		idToEClass
+				.put(Formula.LT, FormulasPackage.eINSTANCE.getLessPredicate());
+		idToEClass.put(Formula.LE, FormulasPackage.eINSTANCE
+				.getLessEqualPredicate());
+		idToEClass.put(Formula.GT, FormulasPackage.eINSTANCE
+				.getGreaterPredicate());
+		idToEClass.put(Formula.GE, FormulasPackage.eINSTANCE
+				.getGreaterEqualPredicate());
+		idToEClass.put(Formula.IN, FormulasPackage.eINSTANCE
+				.getBelongPredicate());
+		idToEClass.put(Formula.NOTIN, FormulasPackage.eINSTANCE
+				.getNotBelongPredicate());
+		idToEClass.put(Formula.SUBSET, FormulasPackage.eINSTANCE
+				.getSubsetStrictPredicate());
+		idToEClass.put(Formula.NOTSUBSET, FormulasPackage.eINSTANCE
+				.getNotSubsetStrictPredicate());
+		idToEClass.put(Formula.SUBSETEQ, FormulasPackage.eINSTANCE
+				.getSubsetPredicate());
+		idToEClass.put(Formula.NOTSUBSETEQ, FormulasPackage.eINSTANCE
+				.getNotSubsetPredicate());
+		idToEClass.put(Formula.MAPSTO, FormulasPackage.eINSTANCE
+				.getMapletExpression());
+		idToEClass.put(Formula.REL, FormulasPackage.eINSTANCE
+				.getRelationExpression());
+		idToEClass.put(Formula.TREL, FormulasPackage.eINSTANCE
+				.getTotalRelationExpression());
+		idToEClass.put(Formula.SREL, FormulasPackage.eINSTANCE
+				.getSurjectiveRelationExpression());
+		idToEClass.put(Formula.STREL, FormulasPackage.eINSTANCE
+				.getTotalSurjectiveRelationExpression());
+		idToEClass.put(Formula.PFUN, FormulasPackage.eINSTANCE
+				.getPartialFunctionExpression());
+		idToEClass.put(Formula.TFUN, FormulasPackage.eINSTANCE
+				.getTotalFunctionExpression());
+		idToEClass.put(Formula.PINJ, FormulasPackage.eINSTANCE
+				.getPartialInjectionExpression());
+		idToEClass.put(Formula.TINJ, FormulasPackage.eINSTANCE
+				.getTotalInjectionExpression());
+		idToEClass.put(Formula.PSUR, FormulasPackage.eINSTANCE
+				.getPartialSurjectionExpression());
+		idToEClass.put(Formula.TSUR, FormulasPackage.eINSTANCE
+				.getTotalSurjectionExpression());
+		idToEClass.put(Formula.TBIJ, FormulasPackage.eINSTANCE
+				.getTotalBijectionExpression());
+		idToEClass.put(Formula.SETMINUS, FormulasPackage.eINSTANCE
+				.getSetSubtractionExpression());
+		idToEClass.put(Formula.CPROD, FormulasPackage.eINSTANCE
+				.getCartesianProductExpression());
+		idToEClass.put(Formula.DPROD, FormulasPackage.eINSTANCE
+				.getDirectProductExpression());
+		idToEClass.put(Formula.DOMRES, FormulasPackage.eINSTANCE
+				.getDomainRestrictionExpression());
+		idToEClass.put(Formula.DOMSUB, FormulasPackage.eINSTANCE
+				.getDomainSubtractionExpression());
+		idToEClass.put(Formula.RANRES, FormulasPackage.eINSTANCE
+				.getRangeRestrictionExpression());
+		idToEClass.put(Formula.RANSUB, FormulasPackage.eINSTANCE
+				.getRangeSubtractionExpression());
+		idToEClass.put(Formula.UPTO, FormulasPackage.eINSTANCE
+				.getUptoExpression());
+		idToEClass.put(Formula.MINUS, FormulasPackage.eINSTANCE
+				.getSubtractExpression());
+		idToEClass.put(Formula.DIV, FormulasPackage.eINSTANCE
+				.getDivisionExpression());
+		idToEClass.put(Formula.MOD, FormulasPackage.eINSTANCE
+				.getModuloExpression());
+		idToEClass.put(Formula.EXPN, FormulasPackage.eINSTANCE
+				.getExponentiationExpression());
+		idToEClass.put(Formula.FUNIMAGE, FormulasPackage.eINSTANCE
+				.getFunctionExpression());
+		idToEClass.put(Formula.RELIMAGE, FormulasPackage.eINSTANCE
+				.getImageExpression());
+		idToEClass.put(Formula.LIMP, FormulasPackage.eINSTANCE
+				.getImplicationPredicate());
+		idToEClass.put(Formula.LEQV, FormulasPackage.eINSTANCE
+				.getEquivalencePredicate());
+
+		// Multi operand predicates/expressions
+		idToEClass.put(Formula.BUNION, FormulasPackage.eINSTANCE
+				.getUnionExpression());
+		idToEClass.put(Formula.BINTER, FormulasPackage.eINSTANCE
+				.getIntersectionExpression());
+		idToEClass.put(Formula.BCOMP, FormulasPackage.eINSTANCE
+				.getBackwardCompositionExpression());
+		idToEClass.put(Formula.FCOMP, FormulasPackage.eINSTANCE
+				.getForwardCompositionExpression());
+		idToEClass.put(Formula.OVR, FormulasPackage.eINSTANCE
+				.getRelationalOverridingExpression());
+		idToEClass.put(Formula.PLUS, FormulasPackage.eINSTANCE
+				.getAddExpression());
+		idToEClass.put(Formula.MUL, FormulasPackage.eINSTANCE
+				.getMulExpression());
+		idToEClass.put(Formula.LAND, FormulasPackage.eINSTANCE
+				.getAndPredicate());
+		idToEClass.put(Formula.LOR, FormulasPackage.eINSTANCE.getOrPredicate());
+		idToEClass.put(Formula.SETEXT, FormulasPackage.eINSTANCE
+				.getSetExpression());
+		idToEClass.put(Formula.KPARTITION, FormulasPackage.eINSTANCE
+				.getPartitionPredicate());
+
+		// Quantifiers:
+		// FORALL, EXISTS
+		idToEClass.put(Formula.FORALL, FormulasPackage.eINSTANCE
+				.getForallPredicate());
+		idToEClass.put(Formula.EXISTS, FormulasPackage.eINSTANCE
+				.getExistPredicate());
+		// cannot decide QUNION and QINTER here
+	}
+
+	public BExpressionResolved convert(final Expression rodinExpr,
+			final int offset) {
+		traverseFormula(rodinExpr, offset);
+		return (BExpressionResolved) stack.pop();
+	}
+
+	public BPredicateResolved convert(final Predicate predicate,
+			final int offset) {
+		traverseFormula(predicate, offset);
+		return (BPredicateResolved) stack.pop();
+	}
+
+	public BAssignmentResolved convert(final Assignment assignment,
+			final int offset) {
+		traverseFormula(assignment, offset);
+		return (BAssignmentResolved) stack.pop();
+	}
+
+	@SuppressWarnings("unchecked")
+	private void traverseFormula(final Formula rodinFormula, final int offset) {
+		emfToRodinElements = new HashMap<BFormula, Formula>();
+
+		textOffset = offset;
+
+		rodinFormula.accept(this);
+		assert stack.size() == 1;
+	}
+
+	/**
+	 * Handles a newly created emf node, i.e., push to stack, store mapping to
+	 * Rodin element
+	 * 
+	 * @param node
+	 * @param rodinFormula
+	 */
+	@SuppressWarnings("unchecked")
+	private void storeNode(final BFormula node, final Formula rodinFormula) {
+		annotatePosition(node, rodinFormula);
+		emfToRodinElements.put(node, rodinFormula);
+		stack.push(node);
+	}
+
+	@SuppressWarnings("unchecked")
+	public Map<BFormula, Formula> getEmfToRodinMapping() {
+		return emfToRodinElements;
+	}
+
+	public void visitAssociativeExpression(
+			final AssociativeExpression expression) {
+		handleMultiChildren(expression, expression.getChildren());
+	}
+
+	public void visitAssociativePredicate(final AssociativePredicate predicate) {
+		handleMultiChildren(predicate, predicate.getChildren());
+	}
+
+	public void visitAtomicExpression(final AtomicExpression expression) {
+		final EClass eClass = getMatchingEClass(expression);
+		final EObject eObject = FormulasFactory.eINSTANCE.create(eClass);
+		storeNode((BFormula) eObject, expression);
+	}
+
+	public void visitBecomesEqualTo(final BecomesEqualTo assignment) {
+		final FreeIdentifier[] identifiers = assignment
+				.getAssignedIdentifiers();
+		visitChildren(identifiers);
+
+		final Expression[] expressions = assignment.getExpressions();
+		visitChildren(expressions);
+
+		final BecomesEqualToAssignment newNode = FormulasFactory.eINSTANCE
+				.createBecomesEqualToAssignment();
+
+		// attach expression children
+		final EList<BExpressionResolved> exprChildren = newNode
+				.getExpressions();
+		for (int i = 0; i < expressions.length; i++) {
+			exprChildren.add(0, (BExpressionResolved) stack.pop());
+		}
+
+		// attach identifier children
+		final EList<IdentifierExpression> identChildren = newNode
+				.getIdentifiers();
+		for (int i = 0; i < identifiers.length; i++) {
+			identChildren.add(0, (IdentifierExpression) stack.pop());
+		}
+
+		storeNode(newNode, assignment);
+	}
+
+	public void visitBecomesMemberOf(final BecomesMemberOf assignment) {
+		final FreeIdentifier[] identifiers = assignment
+				.getAssignedIdentifiers();
+		visitChildren(identifiers);
+
+		final Expression expression = assignment.getSet();
+		visitChildren(expression);
+
+		final BecomesMemberOfAssignment newNode = FormulasFactory.eINSTANCE
+				.createBecomesMemberOfAssignment();
+
+		// attach expression child
+		newNode.setExpression((BExpressionResolved) stack.pop());
+
+		// attach identifier children
+		final EList<IdentifierExpression> identChildren = newNode
+				.getIdentifiers();
+		for (int i = 0; i < identifiers.length; i++) {
+			identChildren.add(0, (IdentifierExpression) stack.pop());
+		}
+
+		storeNode(newNode, assignment);
+	}
+
+	public void visitBecomesSuchThat(final BecomesSuchThat assignment) {
+		final FreeIdentifier[] identifiers = assignment
+				.getAssignedIdentifiers();
+		visitChildren(identifiers);
+
+		// add bound identifier declarations
+		final List<String> primedIdents = new LinkedList<String>();
+		for (int i = 0; i < identifiers.length; i++) {
+			primedIdents.add(identifiers[i].getName() + "'");
+		}
+		boundIdDeclStack.push(primedIdents);
+
+		final Predicate predicate = assignment.getCondition();
+		visitChildren(predicate);
+
+		boundIdDeclStack.pop();
+
+		final BecomesSuchThatAssignment newNode = FormulasFactory.eINSTANCE
+				.createBecomesSuchThatAssignment();
+
+		// attach expression child
+		newNode.setPredicate((BPredicateResolved) stack.pop());
+
+		// attach identifier children
+		final EList<IdentifierExpression> identChildren = newNode
+				.getIdentifiers();
+		for (int i = 0; i < identifiers.length; i++) {
+			identChildren.add(0, (IdentifierExpression) stack.pop());
+		}
+
+		storeNode(newNode, assignment);
+	}
+
+	public void visitBinaryExpression(final BinaryExpression expression) {
+		handleTwoChildren(expression, expression.getLeft(), expression
+				.getRight());
+	}
+
+	public void visitBinaryPredicate(final BinaryPredicate predicate) {
+		handleTwoChildren(predicate, predicate.getLeft(), predicate.getRight());
+	}
+
+	public void visitBoolExpression(final BoolExpression expression) {
+		handleSingleChild(expression, expression.getPredicate());
+	}
+
+	public void visitBoundIdentDecl(final BoundIdentDecl boundIdentDecl) {
+		final BoundIdentifierExpression node = FormulasFactory.eINSTANCE
+				.createBoundIdentifierExpression();
+		node.setName(boundIdentDecl.getName());
+		storeNode(node, boundIdentDecl);
+	}
+
+	public void visitBoundIdentifier(final BoundIdentifier identifierExpression) {
+		final BoundIdentifierExpression node = FormulasFactory.eINSTANCE
+				.createBoundIdentifierExpression();
+
+		final List<String> identDecls = boundIdDeclStack.peek();
+		final int index = identifierExpression.getBoundIndex();
+		final String name = identDecls.get(index);
+		node.setName(name);
+
+		storeNode(node, identifierExpression);
+	}
+
+	public void visitFreeIdentifier(final FreeIdentifier identifierExpression) {
+		final IdentifierExpression node = FormulasFactory.eINSTANCE
+				.createIdentifierExpression();
+		node.setName(identifierExpression.getName());
+		storeNode(node, identifierExpression);
+	}
+
+	public void visitIntegerLiteral(final IntegerLiteral expression) {
+		final IntegerLiteralExpression node = FormulasFactory.eINSTANCE
+				.createIntegerLiteralExpression();
+		node.setNumber(expression.getValue());
+
+		storeNode(node, expression);
+	}
+
+	public void visitLiteralPredicate(final LiteralPredicate predicate) {
+		final Constant node = (Constant) FormulasFactory.eINSTANCE
+				.create(getMatchingEClass(predicate));
+		storeNode(node, predicate);
+	}
+
+	public void visitMultiplePredicate(final MultiplePredicate predicate) {
+		handleMultiChildren(predicate, predicate.getChildren());
+	}
+
+	public void visitQuantifiedExpression(final QuantifiedExpression expression) {
+		final BoundIdentDecl[] localIdentifiers = expression
+				.getBoundIdentDecls();
+		boundIdDeclStack.push(convertIdentDecl(localIdentifiers));
+		for (final BoundIdentDecl boundIdentDecl : localIdentifiers) {
+			boundIdentDecl.accept(this);
+		}
+
+		expression.getPredicate().accept(this);
+		expression.getExpression().accept(this);
+		boundIdDeclStack.pop();
+
+		final boolean isFullType = localIdentifiers.length > 0;
+		BExpressionResolved node = null;
+		EList<IdentifierExpression> identifiers = null;
+
+		switch (expression.getTag()) {
+		case Formula.QUNION:
+			if (isFullType) {
+				final QuantifiedUnionExpression1 newNode = FormulasFactory.eINSTANCE
+						.createQuantifiedUnionExpression1();
+				newNode.setExpression((BExpressionResolved) stack.pop());
+				newNode.setPredicate((BPredicateResolved) stack.pop());
+				identifiers = newNode.getIdentifiers();
+				node = newNode;
+			} else {
+				final QuantifiedUnionExpression2 newNode = FormulasFactory.eINSTANCE
+						.createQuantifiedUnionExpression2();
+				newNode.setExpression((BExpressionResolved) stack.pop());
+				newNode.setPredicate((BPredicateResolved) stack.pop());
+				node = newNode;
+			}
+			break;
+		case Formula.QINTER:
+			if (isFullType) {
+				final QuantifiedIntersectionExpression1 newNode = FormulasFactory.eINSTANCE
+						.createQuantifiedIntersectionExpression1();
+				newNode.setExpression((BExpressionResolved) stack.pop());
+				newNode.setPredicate((BPredicateResolved) stack.pop());
+				identifiers = newNode.getIdentifiers();
+				node = newNode;
+			} else {
+				final QuantifiedIntersectionExpression2 newNode = FormulasFactory.eINSTANCE
+						.createQuantifiedIntersectionExpression2();
+				newNode.setExpression((BExpressionResolved) stack.pop());
+				newNode.setPredicate((BPredicateResolved) stack.pop());
+				node = newNode;
+			}
+			break;
+		case Formula.CSET:
+			if (isFullType) {
+				final SetComprehensionExpression1 newNode = FormulasFactory.eINSTANCE
+						.createSetComprehensionExpression1();
+				newNode.setExpression((BExpressionResolved) stack.pop());
+				newNode.setPredicate((BPredicateResolved) stack.pop());
+				identifiers = newNode.getIdentifiers();
+				node = newNode;
+			} else {
+				final SetComprehensionExpression2 newNode = FormulasFactory.eINSTANCE
+						.createSetComprehensionExpression2();
+				newNode.setExpression((BExpressionResolved) stack.pop());
+				newNode.setPredicate((BPredicateResolved) stack.pop());
+				node = newNode;
+			}
+			break;
+
+		default:
+			// TODO handle unexpected tags
+			break;
+		}
+
+		// attach declared local variables (coming in reverse order from stack)
+		for (int i = 0; i < localIdentifiers.length; i++) {
+			identifiers.add(0, (IdentifierExpression) stack.pop());
+		}
+
+		storeNode(node, expression);
+	}
+
+	public void visitQuantifiedPredicate(final QuantifiedPredicate predicate) {
+		// visit children
+		final BoundIdentDecl[] localIdentifiers = predicate
+				.getBoundIdentDecls();
+		boundIdDeclStack.push(convertIdentDecl(localIdentifiers));
+		for (final BoundIdentDecl boundIdentDecl : localIdentifiers) {
+			boundIdentDecl.accept(this);
+		}
+
+		predicate.getPredicate().accept(this);
+		boundIdDeclStack.pop();
+
+		final BFormula node = (BFormula) FormulasFactory.eINSTANCE
+				.create(getMatchingEClass(predicate));
+		EList<BoundIdentifierExpression> identifiers = null;
+
+		switch (predicate.getTag()) {
+		// FORALL, EXISTS
+		case Formula.FORALL:
+			final ForallPredicate forallPred = (ForallPredicate) node;
+			identifiers = forallPred.getIdentifiers();
+			forallPred.setPredicate((BPredicateResolved) stack.pop());
+			break;
+		case Formula.EXISTS:
+			final ExistPredicate existPred = (ExistPredicate) node;
+			identifiers = existPred.getIdentifiers();
+			existPred.setPredicate((BPredicateResolved) stack.pop());
+			break;
+
+		default:
+			// TODO handle unexpected tags
+			break;
+		}
+
+		// attach declared local variables (coming in reverse order from stack)
+		for (int i = 0; i < localIdentifiers.length; i++) {
+			identifiers.add(0, (BoundIdentifierExpression) stack.pop());
+		}
+
+		storeNode(node, predicate);
+	}
+
+	public void visitRelationalPredicate(final RelationalPredicate predicate) {
+		handleTwoChildren(predicate, predicate.getLeft(), predicate.getRight());
+	}
+
+	public void visitSetExtension(final SetExtension expression) {
+		handleMultiChildren(expression, expression.getMembers());
+	}
+
+	public void visitSimplePredicate(final SimplePredicate predicate) {
+		handleSingleChild(predicate, predicate.getExpression());
+	}
+
+	public void visitUnaryExpression(final UnaryExpression expression) {
+		handleSingleChild(expression, expression.getChild());
+	}
+
+	public void visitUnaryPredicate(final UnaryPredicate predicate) {
+		handleSingleChild(predicate, predicate.getChild());
+	}
+
+	@SuppressWarnings("unchecked")
+	private void handleSingleChild(final Formula rodinNode, final Formula child) {
+		// visit child
+		child.accept(this);
+
+		// create node itself
+		final UnaryOperator node = (UnaryOperator) FormulasFactory.eINSTANCE
+				.create(getMatchingEClass(rodinNode));
+
+		// add the children nodes
+		node.setChild(stack.pop());
+
+		storeNode(node, rodinNode);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void handleTwoChildren(final Formula rodinNode,
+			final Formula leftChild, final Formula rightChild) {
+		// visit children
+		visitChildren(leftChild);
+		visitChildren(rightChild);
+
+		// create node itself
+		final BinaryOperator node = (BinaryOperator) FormulasFactory.eINSTANCE
+				.create(getMatchingEClass(rodinNode));
+
+		// add the children nodes
+		node.setRight(stack.pop());
+		node.setLeft(stack.pop());
+
+		storeNode(node, rodinNode);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void handleMultiChildren(final Formula rodinNode,
+			final Formula[] rodinChildren) {
+		visitChildren(rodinChildren);
+
+		// create node itself
+		final MultiOperand node = (MultiOperand) FormulasFactory.eINSTANCE
+				.create(getMatchingEClass(rodinNode));
+
+		// add the children nodes
+		final EList<BFormula> children = node.getChildren();
+		for (int i = 0; i < rodinChildren.length; i++) {
+			children.add(0, stack.pop());
+		}
+
+		storeNode(node, rodinNode);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void visitChildren(final Formula[] rodinChildren) {
+		for (final Formula child : rodinChildren) {
+			child.accept(this);
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	private void visitChildren(final Formula child) {
+		child.accept(this);
+	}
+
+	private List<String> convertIdentDecl(final BoundIdentDecl[] identDecl) {
+		LinkedList<String> result = null;
+		if (!boundIdDeclStack.isEmpty()) {
+			result = new LinkedList<String>(boundIdDeclStack.peek());
+		} else {
+			result = new LinkedList<String>();
+		}
+
+		for (int i = 0; i < identDecl.length; i++) {
+			result.addFirst(identDecl[i].getName());
+		}
+
+		return result;
+	}
+
+	@SuppressWarnings("unchecked")
+	private void annotatePosition(final BFormula node,
+			final Formula rodinFormula) {
+
+		final SourceLocation location = rodinFormula.getSourceLocation();
+		if (location != null) {
+			final int startPos = location.getStart();
+			final int endPos = location.getEnd();
+
+			TextPositionUtil.annotatePosition(node, textOffset + startPos,
+					endPos - startPos + 1);
+		}
+
+		/*
+		 * FIXME find out why SourceLocation is missing sometimes
+		 */
+	}
+
+	@SuppressWarnings("unchecked")
+	private EClass getMatchingEClass(final Formula formula) {
+		final EClass eClass = idToEClass.get(formula.getTag());
+
+		if (eClass == null) {
+			final String message = "Unknown Rodin formula type: ["
+					+ formula.getTag() + "] " + formula.toString();
+
+			TextToolsPlugin.getDefault().getLog().log(
+					new Status(IStatus.ERROR, TextToolsPlugin.PLUGIN_ID,
+							message));
+
+			throw new UnsupportedOperationException(message);
+		}
+
+		return eClass;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/internal/parsing/TransformationVisitor.java b/org.eventb.texttools/src/org/eventb/texttools/internal/parsing/TransformationVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..22f8bbbec9ee05a26c1fcfa8da5d3b4c9bc8cdfc
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/internal/parsing/TransformationVisitor.java
@@ -0,0 +1,435 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.internal.parsing;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eventb.emf.core.EventBCommented;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.Axiom;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.context.ContextFactory;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.Convergence;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Invariant;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.MachineFactory;
+import org.eventb.emf.core.machine.Variant;
+import org.eventb.texttools.TextPositionUtil;
+import org.eventb.texttools.model.texttools.TextRange;
+import org.eventb.texttools.model.texttools.TexttoolsFactory;
+
+import de.be4.eventb.core.parser.analysis.DepthFirstAdapter;
+import de.be4.eventb.core.parser.node.AAction;
+import de.be4.eventb.core.parser.node.AAnticipatedConvergence;
+import de.be4.eventb.core.parser.node.AAxiom;
+import de.be4.eventb.core.parser.node.ACarrierSet;
+import de.be4.eventb.core.parser.node.AConstant;
+import de.be4.eventb.core.parser.node.AContextParseUnit;
+import de.be4.eventb.core.parser.node.AConvergentConvergence;
+import de.be4.eventb.core.parser.node.ADerivedAxiom;
+import de.be4.eventb.core.parser.node.ADerivedInvariant;
+import de.be4.eventb.core.parser.node.AEvent;
+import de.be4.eventb.core.parser.node.AExtendedEventRefinement;
+import de.be4.eventb.core.parser.node.AGuard;
+import de.be4.eventb.core.parser.node.AInvariant;
+import de.be4.eventb.core.parser.node.AMachineParseUnit;
+import de.be4.eventb.core.parser.node.AOrdinaryConvergence;
+import de.be4.eventb.core.parser.node.AParameter;
+import de.be4.eventb.core.parser.node.ARefinesEventRefinement;
+import de.be4.eventb.core.parser.node.AVariable;
+import de.be4.eventb.core.parser.node.AVariant;
+import de.be4.eventb.core.parser.node.AWitness;
+import de.be4.eventb.core.parser.node.Node;
+import de.be4.eventb.core.parser.node.PEventRefinement;
+import de.be4.eventb.core.parser.node.TComment;
+import de.be4.eventb.core.parser.node.TFormula;
+import de.be4.eventb.core.parser.node.TIdentifierLiteral;
+import de.be4.eventb.core.parser.node.TLabel;
+import de.be4.eventb.core.parser.node.Token;
+import de.hhu.stups.sablecc.patch.IToken;
+import de.hhu.stups.sablecc.patch.PositionedNode;
+import de.hhu.stups.sablecc.patch.SourcePosition;
+
+public class TransformationVisitor extends DepthFirstAdapter {
+
+	private IDocument document;
+
+	private final Stack<EventBObject> stack = new Stack<EventBObject>();
+	private final Stack<Convergence> convergenceStack = new Stack<Convergence>();
+
+	@SuppressWarnings("unchecked")
+	public <T extends EventBObject> T transform(final Node rootNode,
+			final IDocument document) {
+		this.document = document;
+		stack.clear();
+		convergenceStack.clear();
+
+		rootNode.apply(this);
+
+		this.document = null;
+
+		assert stack.size() == 1;
+		return (T) stack.pop();
+	}
+
+	@Override
+	public void outAMachineParseUnit(final AMachineParseUnit node) {
+		final Machine newNode = MachineFactory.eINSTANCE.createMachine();
+		TextPositionUtil.annotatePosition(newNode, createTextRange(node));
+
+		handleNamedAndCommented(newNode, node, node.getName(), node
+				.getComments(), false);
+		handleList(newNode, newNode.getRefinesNames(), node.getRefinesNames());
+		handleList(newNode, newNode.getSeesNames(), node.getSeenNames());
+
+		/*
+		 * Child elements have been visited before and are lying on the stack in
+		 * reverse order.
+		 */
+		handleList(newNode.getEvents(), node.getEvents().size());
+
+		// variant
+		if (node.getVariant() != null) {
+			newNode.setVariant((Variant) stack.pop());
+		}
+
+		// TODO theorems
+		// final EList<Theorem> theorems = newNode.getTheorems();
+		// handleList(theorems, node.getTheorems().size());
+
+		handleList(newNode.getInvariants(), node.getInvariants().size());
+		handleList(newNode.getVariables(), node.getVariables().size());
+
+		stack.push(newNode);
+	}
+
+	@Override
+	public void outAContextParseUnit(final AContextParseUnit node) {
+		final Context newNode = ContextFactory.eINSTANCE.createContext();
+		TextPositionUtil.annotatePosition(newNode, createTextRange(node));
+
+		handleNamedAndCommented(newNode, node, node.getName(), node
+				.getComments(), false);
+		handleList(newNode, newNode.getExtendsNames(), node.getExtendsNames());
+
+		/*
+		 * Child elements have been visited before and are lying on the stack in
+		 * reverse order.
+		 */
+		handleList(newNode.getAxioms(), node.getAxioms().size());
+
+		// TODO theorems
+		// final EList<Theorem> theorems = newNode.getTheorems();
+		// handleList(theorems, node.getTheorems().size());
+		handleList(newNode.getConstants(), node.getConstants().size());
+		handleList(newNode.getSets(), node.getSets().size());
+
+		stack.push(newNode);
+
+	}
+
+	@Override
+	public void outAEvent(final AEvent node) {
+		final Event newNode = MachineFactory.eINSTANCE.createEvent();
+		TextPositionUtil.annotatePosition(newNode, createTextRange(node));
+
+		handleNamedAndCommented(newNode, node, node.getName(), node
+				.getComments(), false);
+
+		if (node.getConvergence() != null) {
+			newNode.setConvergence(convergenceStack.pop());
+		}
+
+		// event refinement is a bit more complicated
+		final PEventRefinement refinement = node.getRefinement();
+		if (refinement != null) {
+			if (refinement instanceof ARefinesEventRefinement) {
+				handleList(newNode, newNode.getRefinesNames(),
+						((ARefinesEventRefinement) refinement).getNames());
+				newNode.setExtended(false);
+			} else {
+				final AExtendedEventRefinement extended = (AExtendedEventRefinement) refinement;
+				final TIdentifierLiteral extendsToken = extended.getName();
+				final String extendsName = extendsToken.getText();
+				newNode.getRefinesNames().add(extendsName);
+				storePosition(extendsName, newNode, extendsToken);
+				newNode.setExtended(true);
+			}
+		} else {
+			newNode.setExtended(false);
+		}
+
+		/*
+		 * Child elements have been visited before and are lying on the stack in
+		 * reverse order.
+		 */
+		handleList(newNode.getActions(), node.getActions().size());
+		handleList(newNode.getWitnesses(), node.getWitnesses().size());
+		handleList(newNode.getGuards(), node.getGuards().size());
+		handleList(newNode.getParameters(), node.getParameters().size());
+
+		stack.push(newNode);
+
+	}
+
+	@Override
+	public void outAVariable(final AVariable node) {
+		handleNamedAndCommented(MachineFactory.eINSTANCE.createVariable(),
+				node, node.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outAInvariant(final AInvariant node) {
+		handleLabeledPredicate(MachineFactory.eINSTANCE.createInvariant(),
+				node, node.getPredicate(), node.getName(), node.getComments(),
+				true);
+	}
+
+	@Override
+	public void outADerivedInvariant(final ADerivedInvariant node) {
+		final Invariant invariant = MachineFactory.eINSTANCE.createInvariant();
+		invariant.setTheorem(true);
+		handleLabeledPredicate(invariant, node, node.getPredicate(), node
+				.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outAVariant(final AVariant node) {
+		final Variant newNode = MachineFactory.eINSTANCE.createVariant();
+		TextPositionUtil.annotatePosition(newNode, createTextRange(node));
+
+		handleComment(newNode, node.getComments());
+
+		final TFormula exprToken = node.getExpression();
+		final String exprString = exprToken.getText();
+		newNode.setExpression(exprString);
+		storePosition(exprString, newNode, exprToken);
+
+		stack.push(newNode);
+	}
+
+	@Override
+	public void outAOrdinaryConvergence(final AOrdinaryConvergence node) {
+		convergenceStack.push(Convergence.ORDINARY);
+	}
+
+	@Override
+	public void outAAnticipatedConvergence(final AAnticipatedConvergence node) {
+		convergenceStack.push(Convergence.ANTICIPATED);
+	}
+
+	@Override
+	public void outAConvergentConvergence(final AConvergentConvergence node) {
+		convergenceStack.push(Convergence.CONVERGENT);
+	}
+
+	@Override
+	public void outAAction(final AAction node) {
+		final Action newNode = MachineFactory.eINSTANCE.createAction();
+		handleNamedAndCommented(newNode, node, node.getName(), node
+				.getComments(), true);
+
+		final TFormula actionToken = node.getAction();
+		final String actionString = actionToken.getText();
+		newNode.setAction(actionString);
+		storePosition(actionString, newNode, actionToken);
+	}
+
+	@Override
+	public void outAWitness(final AWitness node) {
+		handleLabeledPredicate(MachineFactory.eINSTANCE.createWitness(), node,
+				node.getPredicate(), node.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outAGuard(final AGuard node) {
+		handleLabeledPredicate(MachineFactory.eINSTANCE.createGuard(), node,
+				node.getPredicate(), node.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outAParameter(final AParameter node) {
+		handleNamedAndCommented(MachineFactory.eINSTANCE.createParameter(),
+				node, node.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outAAxiom(final AAxiom node) {
+		handleLabeledPredicate(ContextFactory.eINSTANCE.createAxiom(), node,
+				node.getPredicate(), node.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outADerivedAxiom(final ADerivedAxiom node) {
+		final Axiom axiom = ContextFactory.eINSTANCE.createAxiom();
+		axiom.setTheorem(true);
+		handleLabeledPredicate(axiom, node, node.getPredicate(),
+				node.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outACarrierSet(final ACarrierSet node) {
+		handleNamedAndCommented(ContextFactory.eINSTANCE.createCarrierSet(),
+				node, node.getName(), node.getComments(), true);
+	}
+
+	@Override
+	public void outAConstant(final AConstant node) {
+		handleNamedAndCommented(ContextFactory.eINSTANCE.createConstant(),
+				node, node.getName(), node.getComments(), true);
+	}
+
+	private void handleNamedAndCommented(final EventBNamed newNode,
+			final PositionedNode node, final Token name,
+			final LinkedList<TComment> comments, final boolean store) {
+		if (store) {
+			/*
+			 * Need to annotate position before continuing because substrings
+			 * will need the annotation.
+			 */
+			TextPositionUtil.annotatePosition((EventBElement) newNode,
+					createTextRange(node));
+		}
+
+		handleComment((EventBCommented) newNode, comments);
+		handleName(newNode, name);
+
+		if (store) {
+			stack.push((EventBElement) newNode);
+		}
+	}
+
+	private void handleLabeledPredicate(final EventBNamedCommentedPredicateElement newNode,
+			final Node node, final TFormula predicate, final TLabel name,
+			final LinkedList<TComment> comments, final boolean store) {
+		handleNamedAndCommented(newNode, node, name, comments, store);
+
+		final String predText = predicate.getText();
+		newNode.setPredicate(predText);
+		storePosition(predText, newNode, predicate);
+	}
+
+	@SuppressWarnings("unchecked")
+	private <T extends EventBObject> void handleList(final EList<T> targetList,
+			final int childrenNumber) {
+		for (int i = 0; i < childrenNumber; i++) {
+			final T childNode = (T) stack.pop();
+			targetList.add(0, childNode);
+		}
+	}
+
+	private <T> void handleList(final EventBObject emfParent,
+			final EList<String> targetList,
+			final List<TIdentifierLiteral> children) {
+		for (final TIdentifierLiteral token : children) {
+			final String text = token.getText();
+			targetList.add(text);
+			storePosition(text, emfParent, token);
+		}
+	}
+
+	private void handleName(final EventBNamed emfElement, final Token nameToken) {
+		if (nameToken != null) {
+			final String name = nameToken.getText();
+			emfElement.setName(name);
+			storePosition(name, (EventBElement) emfElement, nameToken);
+		}
+	}
+
+	private void handleComment(final EventBCommented emfElement,
+			final LinkedList<TComment> comments) {
+		if (comments != null && comments.size() > 0) {
+			final StringBuffer buffer = new StringBuffer();
+
+			final Iterator<TComment> iterator = comments.iterator();
+			while (iterator.hasNext()) {
+				final TComment comment = iterator.next();
+				buffer.append(comment.getText());
+
+				if (iterator.hasNext()) {
+					buffer.append('\n');
+				}
+			}
+
+			final String completeComment = buffer.toString();
+
+			emfElement.setComment(completeComment);
+
+			final TComment firstToken = comments.get(0);
+			final TextRange range = createTextRange(calculateOffset(firstToken
+					.getLine(), firstToken.getPos()), buffer.length());
+			storePosition(completeComment, (EventBElement) emfElement, range);
+		}
+	}
+
+	private void storePosition(final String keyString,
+			final EModelElement emfParent, final TextRange range) {
+		if (keyString == null) {
+			return;
+		}
+
+		if (range != null) {
+			TextPositionUtil.addInternalPosition(emfParent, keyString, range);
+		}
+	}
+
+	private void storePosition(final String keyString,
+			final EModelElement emfParent, final IToken token) {
+		storePosition(keyString, emfParent, createTextRange(token));
+	}
+
+	private TextRange createTextRange(final IToken token) {
+		final int offset = calculateOffset(token.getLine(), token.getPos());
+		final int length = token.getText().length();
+
+		return createTextRange(offset, length);
+	}
+
+	private TextRange createTextRange(final PositionedNode node) {
+		final int offset = calculateOffset(node.getStartPos());
+		final int length = calculateOffset(node.getEndPos()) - offset;
+
+		return createTextRange(offset, length);
+	}
+
+	private TextRange createTextRange(final int offset, final int length) {
+		final TextRange range = TexttoolsFactory.eINSTANCE.createTextRange();
+		range.setOffset(offset);
+		range.setLength(length);
+
+		return range;
+	}
+
+	private int calculateOffset(final int line, final int pos) {
+		try {
+			return document.getLineOffset(line - 1) + pos - 1;
+		} catch (final BadLocationException e) {
+			// IGNORE and return fallback value
+			return 0;
+		}
+	}
+
+	private int calculateOffset(final SourcePosition position) {
+		if (position != null) {
+			return calculateOffset(position.getLine(), position.getPos());
+		}
+
+		return 0;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/merge/EventBMatchEngine.java b/org.eventb.texttools/src/org/eventb/texttools/merge/EventBMatchEngine.java
new file mode 100644
index 0000000000000000000000000000000000000000..34b9e1186c56cb8aadd563abd10376c587058e2b
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/merge/EventBMatchEngine.java
@@ -0,0 +1,172 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.merge;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.emf.compare.FactoryException;
+import org.eclipse.emf.compare.match.engine.IMatchEngine;
+import org.eclipse.emf.compare.match.engine.GenericMatchEngine;
+import org.eclipse.emf.compare.match.internal.statistic.NameSimilarity;
+import org.eclipse.emf.ecore.EObject;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.emf.core.EventBPredicate;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Variant;
+
+/**
+ * A {@link IMatchEngine} which matches EventB emf models. The special structure
+ * of those models is taken into consideration to produce more exact match
+ * models.
+ */
+public class EventBMatchEngine extends GenericMatchEngine {
+	public static final String OPTION_DONT_COMPARE_COMPONENTS = "eventb.dont.compare.components";
+
+	private static final Map<String, Object> defaultOptions = new HashMap<String, Object>();
+
+	static {
+		defaultOptions.put(OPTION_DONT_COMPARE_COMPONENTS, false);
+	}
+
+	public EventBMatchEngine() {
+		// needed for Extension Point
+	}
+
+	public EventBMatchEngine(final Map<String, Object> options) {
+		this.options.putAll(options);
+	}
+
+	@Override
+	public boolean isSimilar(final EObject obj1, final EObject obj2)
+			throws FactoryException {
+		/*
+		 * Test if we consider components as match by default
+		 */
+		final Boolean dontCompareComponents = getOption(OPTION_DONT_COMPARE_COMPONENTS);
+		if (dontCompareComponents && areSameComponentType(obj1, obj2)) {
+			return true;
+		}
+
+		/*
+		 * Only one variant may exist in a model, so two variants are a match
+		 */
+		if (areVariants(obj1, obj2)) {
+			return true;
+		}
+
+		/*
+		 * Our models are typed strictly, that means: different type => no match
+		 */
+		if (!areSameType(obj1, obj2)) {
+			return false;
+		}
+
+		/*
+		 * Improve matching for events with same name
+		 */
+		if (obj1 instanceof Event) {
+			final double similarity = nameSimilarity(obj1, obj2);
+			if (similarity == 1) {
+				return true;
+			}
+		}
+
+		// otherwise use default comparison of GenericMatchEngine
+		return super.isSimilar(obj1, obj2);
+	}
+
+	@Override
+	protected double nameSimilarity(final EObject obj1, final EObject obj2) {
+		if (!areSameType(obj1, obj2)) {
+			return 0d;
+		}
+
+		return NameSimilarity
+				.nameSimilarityMetric(getName(obj1), getName(obj2));
+	}
+
+	@Override
+	protected double contentSimilarity(final EObject obj1, final EObject obj2)
+			throws FactoryException {
+		if (!areSameType(obj1, obj2)) {
+			return 0d;
+		}
+
+		double result = super.contentSimilarity(obj1, obj2);
+
+		if (obj1 instanceof EventBPredicate) {
+			// give predicate equality extra weight
+			result = (result + 2 * contentSimilarity((EventBPredicate) obj1,
+					(EventBPredicate) obj2)) / 3;
+		}
+
+		return result;
+	}
+
+	private double contentSimilarity(final EventBPredicate pred1,
+			final EventBPredicate pred2) {
+		return NameSimilarity.nameSimilarityMetric(pred1.getPredicate(), pred2
+				.getPredicate());
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected <T> T getOption(final String key) throws ClassCastException {
+		if (options.containsKey(key)) {
+			return (T) options.get(key);
+		} else {
+			return (T) defaultOptions.get(key);
+		}
+	}
+
+	private String getName(final EObject object) {
+		/*
+		 * Make sure correct names for EventBNamed elements are used.
+		 */
+		if (object instanceof EventBNamed) {
+			return ((EventBNamed) object).getName();
+		}
+
+		// fallback to default
+		try {
+			return NameSimilarity.findName(object);
+		} catch (final FactoryException e) {
+			return null;
+		}
+	}
+
+	private boolean areSameType(final EObject obj1, final EObject obj2) {
+		return obj1 != null && obj2 != null
+				&& obj1.eClass().equals(obj2.eClass());
+	}
+
+	/**
+	 * Test if both given {@link EObject}s are {@link EventBComponent}s and of
+	 * same type of component, includes <code>null</code> check.
+	 *
+	 * @param obj1
+	 * @param obj2
+	 * @return
+	 */
+	private boolean areSameComponentType(final EObject obj1, final EObject obj2) {
+		return areSameType(obj1, obj2) && obj1 instanceof EventBNamedCommentedComponentElement;
+	}
+
+	/**
+	 * Test if both given {@link EObject}s are of type {@link Variant}, includes
+	 * <code>null</code> check.
+	 *
+	 * @param obj1
+	 * @param obj2
+	 * @return
+	 */
+	private boolean areVariants(final EObject obj1, final EObject obj2) {
+		return areSameType(obj1, obj2) && obj2 instanceof Variant;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/merge/MergerProvider.java b/org.eventb.texttools/src/org/eventb/texttools/merge/MergerProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdf6e8eec2b230709a53adc1b9ffdb1321641ebc
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/merge/MergerProvider.java
@@ -0,0 +1,35 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.merge;
+
+import java.util.Map;
+
+import org.eclipse.emf.compare.diff.merge.IMerger;
+import org.eclipse.emf.compare.diff.merge.IMergerProvider;
+import org.eclipse.emf.compare.diff.metamodel.DiffElement;
+import org.eclipse.emf.compare.diff.metamodel.ReferenceChangeRightTarget;
+import org.eclipse.emf.compare.util.EMFCompareMap;
+
+public class MergerProvider implements IMergerProvider {
+	private Map<Class<? extends DiffElement>, Class<? extends IMerger>> mergerTypes;
+
+	public Map<Class<? extends DiffElement>, Class<? extends IMerger>> getMergers() {
+		if (mergerTypes == null) {
+			mergerTypes = new EMFCompareMap<Class<? extends DiffElement>, Class<? extends IMerger>>();
+
+			mergerTypes.put(ReferenceChangeRightTarget.class,
+					ReferenceChangeRightTargetMerger.class);
+			// mergerTypes.put(ReferenceChangeRightTarget.class,
+			// DefaultMerger.class);
+
+			// TODO maybe we'll need to implement that one too
+			// mergerTypes.put(ReferenceChangeLeftTarget.class,
+			// ReferenceChangeLeftTargetMerger.class);
+		}
+		return mergerTypes;
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/merge/ModelMerge.java b/org.eventb.texttools/src/org/eventb/texttools/merge/ModelMerge.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ca13536b2651e43bcb75a9b38af6834dd7952b0
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/merge/ModelMerge.java
@@ -0,0 +1,249 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.merge;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.compare.diff.merge.service.MergeService;
+import org.eclipse.emf.compare.diff.metamodel.DiffElement;
+import org.eclipse.emf.compare.diff.metamodel.DiffModel;
+import org.eclipse.emf.compare.diff.metamodel.ModelElementChangeLeftTarget;
+import org.eclipse.emf.compare.diff.service.DiffService;
+import org.eclipse.emf.compare.match.MatchOptions;
+import org.eclipse.emf.compare.match.engine.IMatchEngine;
+import org.eclipse.emf.compare.match.metamodel.MatchModel;
+import org.eclipse.emf.compare.match.service.MatchService;
+import org.eclipse.emf.ecore.EAnnotation;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl;
+import org.eventb.emf.core.EventBNamedCommentedComponentElement;
+import org.eventb.emf.core.Extension;
+import org.eventb.emf.formulas.BFormula;
+import org.eventb.texttools.PersistenceHelper;
+import org.eventb.texttools.TextPositionUtil;
+
+/**
+ * <p>
+ * This class helps merging an core model instance which has been created by the
+ * parser back into the original model instance which was created from the
+ * RodinDB.
+ * </p>
+ * <p>
+ * It takes the original version (old version or left version) and a new version
+ * (right version) and merges the right into the left. This means it takes all
+ * changes from the right and applies them to the left. During this process it
+ * preserves those elements that are not part of the core model and have been
+ * added as {@link Extension}s or {@link EAnnotation}s.
+ * </p>
+ */
+public class ModelMerge {
+	private final EventBNamedCommentedComponentElement oldVersion;
+	private final EventBNamedCommentedComponentElement newVersion;
+	private final IResource resource;
+	private final Map<String, Object> matchOptions;
+
+	public ModelMerge(final EventBNamedCommentedComponentElement oldVersion,
+			final EventBNamedCommentedComponentElement newVersion) {
+		this.oldVersion = oldVersion;
+		this.newVersion = newVersion;
+
+		resource = PersistenceHelper.getIResource(oldVersion.eResource());
+
+		/*
+		 * Configure the matching process: We want to match elements in the
+		 * model by their similarity, so we ignore any IDs.
+		 */
+		matchOptions = new HashMap<String, Object>();
+		matchOptions.put(MatchOptions.OPTION_IGNORE_ID, true);
+		matchOptions.put(MatchOptions.OPTION_IGNORE_XMI_ID, true);
+		matchOptions
+				.put(EventBMatchEngine.OPTION_DONT_COMPARE_COMPONENTS, true);
+	}
+
+	/**
+	 * Compares the two model versions that have been given to the constructor
+	 * and merges all changes in the new version into the old version of the
+	 * model. For this process certain changes are ignored:
+	 * <ul>
+	 * <li>RodinInternalAnnotations</li>
+	 * <li>{@link Extension}s which are not {@link BFormula}s (they are changed
+	 * by the text tools parser)</li>
+	 * </ul>
+	 * 
+	 * @throws InterruptedException
+	 */
+	public void applyChanges(final IProgressMonitor monitor)
+			throws InterruptedException {
+		final SubMonitor subMonitor = SubMonitor.convert(monitor,
+				"Analyzing model changes", 4);
+
+		final IMatchEngine matchEngine = MatchService
+				.getBestMatchEngine(resource.getFileExtension());
+
+		// Workaround to make sure the models have an associated resource
+		// See setResourceFile() for Bug info
+
+		// First find the file extension
+		String path = oldVersion.eResource().getURI().path();
+		String extension = path.substring(path.lastIndexOf('.'));
+		File tmpFileNew = null;
+
+		if (newVersion.eResource() == null) {
+			tmpFileNew = setResourceFile(newVersion, extension);
+		}
+
+		matchOptions.put(MatchOptions.OPTION_PROGRESS_MONITOR, subMonitor
+				.newChild(1));
+		final MatchModel matchModel = matchEngine.contentMatch(oldVersion,
+				newVersion, matchOptions);
+
+		final DiffModel diff = getDiffModel(matchModel, subMonitor.newChild(2));
+		final EList<DiffElement> ownedElements = diff.getOwnedElements();
+
+		if (ownedElements.size() > 0) {
+			// MergeService
+			// .merge(new ArrayList<DiffElement>(ownedElements), false);
+
+			MergeService.merge(ownedElements, false);
+
+			matchOptions.put(MatchOptions.OPTION_PROGRESS_MONITOR, subMonitor
+					.newChild(1));
+			subMonitor.worked(1);
+		}
+
+		// cleanup tmp files
+		if (tmpFileNew != null) {
+			EcoreUtil.remove(newVersion);
+			tmpFileNew.delete();
+		}
+	}
+
+	/**
+	 * This method exists to work around a bug in EMF Compare 1.0.1. The compare
+	 * framework only works if a model has an associated resource. see
+	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=258703
+	 */
+	private File setResourceFile(EventBNamedCommentedComponentElement element,
+			String extension) {
+		try {
+			File tmpFile = File.createTempFile("camille-", extension);
+			tmpFile.deleteOnExit();
+			URI uri = URI.createFileURI(tmpFile.getAbsolutePath());
+			Resource resource = new XMLResourceImpl(uri);
+			resource.getContents().add(element);
+			return tmpFile;
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * Do the diff for the {@link MatchModel} and removed ignored elements
+	 * afterwards.
+	 * 
+	 * @param matchModel
+	 * @param monitor
+	 * @return
+	 */
+	private DiffModel getDiffModel(final MatchModel matchModel,
+			final IProgressMonitor monitor) {
+		final SubMonitor subMonitor = SubMonitor.convert(monitor, 2);
+		final DiffModel diff = DiffService.doDiff(matchModel);
+		subMonitor.worked(1);
+
+		final EList<DiffElement> ownedElements = diff.getOwnedElements();
+		final EList<DiffElement> cleanedDiffs = cleanDiffElements(ownedElements);
+		subMonitor.worked(1);
+
+		ownedElements.clear();
+		ownedElements.addAll(cleanedDiffs);
+
+		return diff;
+	}
+
+	/**
+	 * Recursively remove all ignored diff elements.
+	 * 
+	 * @see #isIgnoredDiff(DiffElement)
+	 * @param elements
+	 * @return
+	 */
+	private EList<DiffElement> cleanDiffElements(
+			final EList<DiffElement> elements) {
+		final EList<DiffElement> result = new BasicEList<DiffElement>();
+
+		if (elements != null) {
+			for (final DiffElement diffElement : elements) {
+				if (!isIgnoredDiff(diffElement)) {
+					// add this diff element to result
+					result.add(diffElement);
+
+					// continue clean recursively
+					final EList<DiffElement> subDiffElements = diffElement
+							.getSubDiffElements();
+					final EList<DiffElement> subResult = cleanDiffElements(subDiffElements);
+					subDiffElements.clear();
+					subDiffElements.addAll(subResult);
+				}
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * Returns whether the given {@link DiffElement} should be ignored when
+	 * applying the diff.
+	 * 
+	 * @param diffElement
+	 * @return
+	 */
+	private boolean isIgnoredDiff(final DiffElement diffElement) {
+		if (diffElement instanceof ModelElementChangeLeftTarget) {
+			/*
+			 * EMF elements of the original model (left) show up as
+			 * RemoveModelElement diffs when they are missing in the new version
+			 * (right). If we don't want to remove them for the merged version
+			 * we just ignore the diff element.
+			 */
+			final ModelElementChangeLeftTarget removedDiff = (ModelElementChangeLeftTarget) diffElement;
+			final EObject leftElement = removedDiff.getLeftElement();
+
+			/*
+			 * The original model may contain arbitrary annotations which we
+			 * didn't create or handle.
+			 */
+			if (leftElement instanceof EAnnotation
+					&& !TextPositionUtil.ANNOTATION_TEXTRANGE
+							.equals(((EAnnotation) leftElement).getSource())) {
+				return true;
+			}
+
+			/*
+			 * Ignore all Extensions but our own, i.e. BFormula
+			 */
+			if (leftElement instanceof Extension
+					&& !(leftElement instanceof BFormula)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/merge/ReferenceChangeRightTargetMerger.java b/org.eventb.texttools/src/org/eventb/texttools/merge/ReferenceChangeRightTargetMerger.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d2e93c1596a7e8a8f9da1999054af0427c08a8c
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/merge/ReferenceChangeRightTargetMerger.java
@@ -0,0 +1,204 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.merge;
+
+import java.util.Iterator;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.compare.EMFComparePlugin;
+import org.eclipse.emf.compare.FactoryException;
+import org.eclipse.emf.compare.diff.merge.DefaultMerger;
+import org.eclipse.emf.compare.diff.merge.service.MergeService;
+import org.eclipse.emf.compare.diff.metamodel.DiffElement;
+import org.eclipse.emf.compare.diff.metamodel.ReferenceChangeRightTarget;
+import org.eclipse.emf.compare.util.EFactory;
+import org.eclipse.emf.ecore.EAnnotation;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eventb.emf.core.EventBElement;
+import org.eventb.emf.core.EventBNamedCommentedElement;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Machine;
+
+public class ReferenceChangeRightTargetMerger extends DefaultMerger {
+	@Override
+	public void applyInOrigin() {
+		final ReferenceChangeRightTarget theDiff = (ReferenceChangeRightTarget) diff;
+		final EObject element = theDiff.getLeftElement();
+		final EReference reference = theDiff.getReference();
+
+		final EObject leftTarget = theDiff.getLeftTarget();
+		EObject rightTarget = theDiff.getRightTarget();
+
+
+		if (leftTarget instanceof Machine || rightTarget instanceof Machine) {
+			// only case: refines attribute of a machine
+			copyMachineRef((Machine) element, (Machine) leftTarget,
+					(Machine) rightTarget);
+		} else if (leftTarget instanceof Context
+				|| rightTarget instanceof Context) {
+			copyContextRef(element, (Context) leftTarget, (Context) rightTarget);
+		} else if (leftTarget instanceof Event || rightTarget instanceof Event) {
+			copyEventRef((Event) element, (Event) leftTarget,
+					(Event) rightTarget);
+		} else {
+			/*
+			 * Default implementation copied from
+			 * ReferenceChangeRightTargetMerger (in internal package of
+			 * compare), leave as is...
+			 */
+			MergeService.getCopier(diff).copyReferenceValue(reference, element,
+					rightTarget, leftTarget);
+
+			// We'll now look through this reference's eOpposite as they are
+			// already
+			// taken care of
+			final Iterator<EObject> siblings = getDiffModel().eAllContents();
+			while (siblings.hasNext()) {
+				final DiffElement op = (DiffElement) siblings.next();
+				if (op instanceof ReferenceChangeRightTarget) {
+					final ReferenceChangeRightTarget link = (ReferenceChangeRightTarget) op;
+					// If this is my eOpposite, delete it from the DiffModel
+					// (merged
+					// along with this one)
+					if (link.getReference().equals(reference.getEOpposite())
+							&& link.getLeftTarget().equals(element)) {
+						removeFromContainer(link);
+					}
+				}
+			}
+		}
+
+		super.applyInOrigin();
+	}
+
+	private void printInfo(String label, EObject target, EObject parent) {
+		System.out.println("====== " + label + " ========");
+		if (!(target instanceof EventBNamedCommentedElement)) {
+			System.out.println("  Target not analysed: " + target);
+
+		} else {
+			EventBNamedCommentedElement eventTarget = (EventBNamedCommentedElement) target;
+			System.out.println("  Target: " + eventTarget.doGetName()
+					+ " (proxy: " + eventTarget.eIsProxy() + ")");
+			if (target instanceof Event) {
+				System.out.println("  Refines: "
+						+ ((Event) target).getRefinesNames());
+			}
+			System.out.println("  Resource: " + eventTarget.eResource());
+		}
+
+		if (!(parent instanceof EventBNamedCommentedElement)) {
+			System.out.println("  Element not analysed: " + parent);
+
+		} else {
+			EventBNamedCommentedElement eventElement = (EventBNamedCommentedElement) parent;
+			System.out.println("  Element: " + eventElement.doGetName()
+					+ " (proxy: " + eventElement.eIsProxy() + ")");
+			if (parent instanceof Event) {
+				System.out.println("  Refines: "
+						+ ((Event) parent).getRefinesNames());
+			}
+			System.out.println("  Resource: " + eventElement.eResource());
+		}
+	}
+
+	private void copyMachineRef(final Machine leftParent,
+			final Machine leftTarget, final Machine rightTarget) {
+		if (leftTarget != null && leftTarget.eIsProxy()) {
+			// simply rename in proxy
+			leftTarget.setName(rightTarget.getName());
+		} else {
+			replaceInList(leftParent.getRefines(), leftTarget, rightTarget);
+		}
+	}
+
+	private void copyEventRef(final Event leftParent, final Event leftTarget,
+			final Event rightTarget) {
+		if (leftTarget != null && leftTarget.eIsProxy()) {
+			// simply rename in proxy
+			leftTarget.setName(rightTarget.getName());
+		} else {
+			replaceInList(leftParent.getRefines(), leftTarget, rightTarget);
+		}
+	}
+
+	private void copyContextRef(final EObject leftParent,
+			final Context leftTarget, final Context rightTarget) {
+		if (leftTarget != null && leftTarget.eIsProxy()) {
+			// simply rename proxy
+			leftTarget.setName(rightTarget.getName());
+		} else {
+			if (leftParent instanceof Machine) {
+				// references is a sees in a machine
+				replaceInList(((Machine) leftParent).getSees(), leftTarget,
+						rightTarget);
+			} else if (leftParent instanceof Context) {
+				// references is a extends in context
+				replaceInList(((Context) leftParent).getExtends(), leftTarget,
+						rightTarget);
+			}
+		}
+	}
+
+	static <T extends EventBElement> void replaceInList(
+			final EList<T> refinesList, final T leftTarget, final T rightTarget) {
+		EObject newTarget = EcoreUtil.copy(rightTarget);
+		if (leftTarget != null) {
+			// search old position and replace
+			for (int i = 0; i < refinesList.size(); i++) {
+				if (refinesList.get(i) == leftTarget) {
+					refinesList.set(i, (T) newTarget);
+					break;
+				}
+			}
+		} else {
+			// just add reference
+			// TODO Do we need to care about the position?
+			refinesList.add((T) newTarget);
+		}
+	}
+
+	@Override
+	public void undoInTarget() {
+		final ReferenceChangeRightTarget theDiff = (ReferenceChangeRightTarget) diff;
+		final EObject element = theDiff.getRightElement();
+		final EObject rightTarget = theDiff.getRightTarget();
+
+		/*
+		 * Default implementation copied from ReferenceChangeRightTargetMerger
+		 * (in internal package of compare), leave as is...
+		 */
+		try {
+			EFactory.eRemove(element, theDiff.getReference().getName(),
+					rightTarget);
+		} catch (final FactoryException e) {
+			EMFComparePlugin.log(e, true);
+		}
+
+		// we should now have a look for AddReferencesLinks needing this object
+		final Iterator<EObject> siblings = getDiffModel().eAllContents();
+		while (siblings.hasNext()) {
+			final DiffElement op = (DiffElement) siblings.next();
+			if (op instanceof ReferenceChangeRightTarget) {
+				final ReferenceChangeRightTarget link = (ReferenceChangeRightTarget) op;
+				// now if I'm in the target References I should put my copy in
+				// the origin
+				if (link.getReference().equals(
+						theDiff.getReference().getEOpposite())
+						&& link.getRightTarget().equals(element)) {
+					removeFromContainer(link);
+				}
+			}
+		}
+
+		super.undoInTarget();
+	}
+
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TextRange.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TextRange.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae33b9d3852254f43f93b831e87fa6e21cd7b655
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TextRange.java
@@ -0,0 +1,118 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools;
+
+import java.util.Map;
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.EObject;
+
+/**
+ * <!-- begin-user-doc -->
+ * A representation of the model object '<em><b>Text Range</b></em>'.
+ * <!-- end-user-doc -->
+ *
+ * <p>
+ * The following features are supported:
+ * <ul>
+ *   <li>{@link org.eventb.texttools.model.texttools.TextRange#getOffset <em>Offset</em>}</li>
+ *   <li>{@link org.eventb.texttools.model.texttools.TextRange#getLength <em>Length</em>}</li>
+ *   <li>{@link org.eventb.texttools.model.texttools.TextRange#getSubTextRanges <em>Sub Text Ranges</em>}</li>
+ * </ul>
+ * </p>
+ *
+ * @see org.eventb.texttools.model.texttools.TexttoolsPackage#getTextRange()
+ * @model
+ * @generated
+ */
+public interface TextRange extends EObject {
+	/**
+	 * Returns the value of the '<em><b>Offset</b></em>' attribute.
+	 * The default value is <code>"0"</code>.
+	 * <!-- begin-user-doc -->
+	 * <p>
+	 * If the meaning of the '<em>Offset</em>' attribute isn't clear,
+	 * there really should be more of a description here...
+	 * </p>
+	 * <!-- end-user-doc -->
+	 * @return the value of the '<em>Offset</em>' attribute.
+	 * @see #setOffset(int)
+	 * @see org.eventb.texttools.model.texttools.TexttoolsPackage#getTextRange_Offset()
+	 * @model default="0" required="true" transient="true"
+	 * @generated
+	 */
+	int getOffset();
+
+	/**
+	 * Sets the value of the '{@link org.eventb.texttools.model.texttools.TextRange#getOffset <em>Offset</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @param value the new value of the '<em>Offset</em>' attribute.
+	 * @see #getOffset()
+	 * @generated
+	 */
+	void setOffset(int value);
+
+	/**
+	 * Returns the value of the '<em><b>Length</b></em>' attribute.
+	 * The default value is <code>"0"</code>.
+	 * <!-- begin-user-doc -->
+	 * <p>
+	 * If the meaning of the '<em>Length</em>' attribute isn't clear,
+	 * there really should be more of a description here...
+	 * </p>
+	 * <!-- end-user-doc -->
+	 * @return the value of the '<em>Length</em>' attribute.
+	 * @see #setLength(int)
+	 * @see org.eventb.texttools.model.texttools.TexttoolsPackage#getTextRange_Length()
+	 * @model default="0" required="true" transient="true"
+	 * @generated
+	 */
+	int getLength();
+
+	/**
+	 * Sets the value of the '{@link org.eventb.texttools.model.texttools.TextRange#getLength <em>Length</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @param value the new value of the '<em>Length</em>' attribute.
+	 * @see #getLength()
+	 * @generated
+	 */
+	void setLength(int value);
+
+	/**
+	 * Returns the value of the '<em><b>Sub Text Ranges</b></em>' attribute.
+	 * <!-- begin-user-doc -->
+	 * <p>
+	 * If the meaning of the '<em>Sub Text Ranges</em>' attribute isn't clear,
+	 * there really should be more of a description here...
+	 * </p>
+	 * <!-- end-user-doc -->
+	 * @return the value of the '<em>Sub Text Ranges</em>' attribute.
+	 * @see #setSubTextRanges(Map)
+	 * @see org.eventb.texttools.model.texttools.TexttoolsPackage#getTextRange_SubTextRanges()
+	 * @model required="true" transient="true"
+	 * @generated
+	 */
+	Map<String, TextRange> getSubTextRanges();
+
+	/**
+	 * Sets the value of the '{@link org.eventb.texttools.model.texttools.TextRange#getSubTextRanges <em>Sub Text Ranges</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @param value the new value of the '<em>Sub Text Ranges</em>' attribute.
+	 * @see #getSubTextRanges()
+	 * @generated
+	 */
+	void setSubTextRanges(Map<String, TextRange> value);
+
+} // TextRange
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TexttoolsFactory.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TexttoolsFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2c2dd968cd45ac06a4212cb2b5d998fd98ff60b
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TexttoolsFactory.java
@@ -0,0 +1,52 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools;
+
+import org.eclipse.emf.ecore.EFactory;
+
+/**
+ * <!-- begin-user-doc -->
+ * The <b>Factory</b> for the model.
+ * It provides a create method for each non-abstract class of the model.
+ * <!-- end-user-doc -->
+ * @see org.eventb.texttools.model.texttools.TexttoolsPackage
+ * @generated
+ */
+public interface TexttoolsFactory extends EFactory {
+	/**
+	 * The singleton instance of the factory.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	TexttoolsFactory eINSTANCE = org.eventb.texttools.model.texttools.impl.TexttoolsFactoryImpl.init();
+
+	/**
+	 * Returns a new object of class '<em>Text Range</em>'.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return a new object of class '<em>Text Range</em>'.
+	 * @generated
+	 */
+	TextRange createTextRange();
+
+	/**
+	 * Returns the package supported by this factory.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the package supported by this factory.
+	 * @generated
+	 */
+	TexttoolsPackage getTexttoolsPackage();
+
+} //TexttoolsFactory
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TexttoolsPackage.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TexttoolsPackage.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8d82d8769478b9e93f04f0920b32bc27bfc4ba3
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/TexttoolsPackage.java
@@ -0,0 +1,216 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools;
+
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcorePackage;
+
+/**
+ * <!-- begin-user-doc -->
+ * The <b>Package</b> for the model.
+ * It contains accessors for the meta objects to represent
+ * <ul>
+ *   <li>each class,</li>
+ *   <li>each feature of each class,</li>
+ *   <li>each enum,</li>
+ *   <li>and each data type</li>
+ * </ul>
+ * <!-- end-user-doc -->
+ * @see org.eventb.texttools.model.texttools.TexttoolsFactory
+ * @model kind="package"
+ * @generated
+ */
+public interface TexttoolsPackage extends EPackage {
+	/**
+	 * The package name.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	String eNAME = "texttools";
+
+	/**
+	 * The package namespace URI.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	String eNS_URI = "http://emf.eventb.org/models/core/texttools";
+
+	/**
+	 * The package namespace name.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	String eNS_PREFIX = "texttools";
+
+	/**
+	 * The singleton instance of the package.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	TexttoolsPackage eINSTANCE = org.eventb.texttools.model.texttools.impl.TexttoolsPackageImpl.init();
+
+	/**
+	 * The meta object id for the '{@link org.eventb.texttools.model.texttools.impl.TextRangeImpl <em>Text Range</em>}' class.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see org.eventb.texttools.model.texttools.impl.TextRangeImpl
+	 * @see org.eventb.texttools.model.texttools.impl.TexttoolsPackageImpl#getTextRange()
+	 * @generated
+	 */
+	int TEXT_RANGE = 0;
+
+	/**
+	 * The feature id for the '<em><b>Offset</b></em>' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int TEXT_RANGE__OFFSET = 0;
+
+	/**
+	 * The feature id for the '<em><b>Length</b></em>' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int TEXT_RANGE__LENGTH = 1;
+
+	/**
+	 * The feature id for the '<em><b>Sub Text Ranges</b></em>' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int TEXT_RANGE__SUB_TEXT_RANGES = 2;
+
+	/**
+	 * The number of structural features of the '<em>Text Range</em>' class.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 * @ordered
+	 */
+	int TEXT_RANGE_FEATURE_COUNT = 3;
+
+
+	/**
+	 * Returns the meta object for class '{@link org.eventb.texttools.model.texttools.TextRange <em>Text Range</em>}'.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for class '<em>Text Range</em>'.
+	 * @see org.eventb.texttools.model.texttools.TextRange
+	 * @generated
+	 */
+	EClass getTextRange();
+
+	/**
+	 * Returns the meta object for the attribute '{@link org.eventb.texttools.model.texttools.TextRange#getOffset <em>Offset</em>}'.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the attribute '<em>Offset</em>'.
+	 * @see org.eventb.texttools.model.texttools.TextRange#getOffset()
+	 * @see #getTextRange()
+	 * @generated
+	 */
+	EAttribute getTextRange_Offset();
+
+	/**
+	 * Returns the meta object for the attribute '{@link org.eventb.texttools.model.texttools.TextRange#getLength <em>Length</em>}'.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the attribute '<em>Length</em>'.
+	 * @see org.eventb.texttools.model.texttools.TextRange#getLength()
+	 * @see #getTextRange()
+	 * @generated
+	 */
+	EAttribute getTextRange_Length();
+
+	/**
+	 * Returns the meta object for the attribute '{@link org.eventb.texttools.model.texttools.TextRange#getSubTextRanges <em>Sub Text Ranges</em>}'.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the meta object for the attribute '<em>Sub Text Ranges</em>'.
+	 * @see org.eventb.texttools.model.texttools.TextRange#getSubTextRanges()
+	 * @see #getTextRange()
+	 * @generated
+	 */
+	EAttribute getTextRange_SubTextRanges();
+
+	/**
+	 * Returns the factory that creates the instances of the model.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the factory that creates the instances of the model.
+	 * @generated
+	 */
+	TexttoolsFactory getTexttoolsFactory();
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * Defines literals for the meta objects that represent
+	 * <ul>
+	 *   <li>each class,</li>
+	 *   <li>each feature of each class,</li>
+	 *   <li>each enum,</li>
+	 *   <li>and each data type</li>
+	 * </ul>
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	interface Literals {
+		/**
+		 * The meta object literal for the '{@link org.eventb.texttools.model.texttools.impl.TextRangeImpl <em>Text Range</em>}' class.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @see org.eventb.texttools.model.texttools.impl.TextRangeImpl
+		 * @see org.eventb.texttools.model.texttools.impl.TexttoolsPackageImpl#getTextRange()
+		 * @generated
+		 */
+		EClass TEXT_RANGE = eINSTANCE.getTextRange();
+
+		/**
+		 * The meta object literal for the '<em><b>Offset</b></em>' attribute feature.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EAttribute TEXT_RANGE__OFFSET = eINSTANCE.getTextRange_Offset();
+
+		/**
+		 * The meta object literal for the '<em><b>Length</b></em>' attribute feature.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EAttribute TEXT_RANGE__LENGTH = eINSTANCE.getTextRange_Length();
+
+		/**
+		 * The meta object literal for the '<em><b>Sub Text Ranges</b></em>' attribute feature.
+		 * <!-- begin-user-doc -->
+		 * <!-- end-user-doc -->
+		 * @generated
+		 */
+		EAttribute TEXT_RANGE__SUB_TEXT_RANGES = eINSTANCE.getTextRange_SubTextRanges();
+
+	}
+
+} //TexttoolsPackage
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TextRangeImpl.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TextRangeImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..a685618972c0273b35fae6e378eee753427e98c2
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TextRangeImpl.java
@@ -0,0 +1,275 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools.impl;
+
+import java.util.Map;
+import org.eclipse.emf.common.notify.Notification;
+
+import org.eclipse.emf.ecore.EClass;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.impl.EModelElementImpl;
+import org.eclipse.emf.ecore.impl.ENotificationImpl;
+import org.eclipse.emf.ecore.impl.EObjectImpl;
+
+import org.eventb.texttools.model.texttools.TextRange;
+import org.eventb.texttools.model.texttools.TexttoolsPackage;
+
+/**
+ * <!-- begin-user-doc -->
+ * An implementation of the model object '<em><b>Text Range</b></em>'.
+ * <!-- end-user-doc -->
+ * <p>
+ * The following features are implemented:
+ * <ul>
+ *   <li>{@link org.eventb.texttools.model.texttools.impl.TextRangeImpl#getOffset <em>Offset</em>}</li>
+ *   <li>{@link org.eventb.texttools.model.texttools.impl.TextRangeImpl#getLength <em>Length</em>}</li>
+ *   <li>{@link org.eventb.texttools.model.texttools.impl.TextRangeImpl#getSubTextRanges <em>Sub Text Ranges</em>}</li>
+ * </ul>
+ * </p>
+ *
+ * @generated
+ */
+public class TextRangeImpl extends EObjectImpl implements TextRange {
+	/**
+	 * The default value of the '{@link #getOffset() <em>Offset</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see #getOffset()
+	 * @generated
+	 * @ordered
+	 */
+	protected static final int OFFSET_EDEFAULT = 0;
+
+	/**
+	 * The cached value of the '{@link #getOffset() <em>Offset</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see #getOffset()
+	 * @generated
+	 * @ordered
+	 */
+	protected int offset = OFFSET_EDEFAULT;
+
+	/**
+	 * The default value of the '{@link #getLength() <em>Length</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see #getLength()
+	 * @generated
+	 * @ordered
+	 */
+	protected static final int LENGTH_EDEFAULT = 0;
+
+	/**
+	 * The cached value of the '{@link #getLength() <em>Length</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see #getLength()
+	 * @generated
+	 * @ordered
+	 */
+	protected int length = LENGTH_EDEFAULT;
+
+	/**
+	 * The cached value of the '{@link #getSubTextRanges() <em>Sub Text Ranges</em>}' attribute.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see #getSubTextRanges()
+	 * @generated
+	 * @ordered
+	 */
+	protected Map<String, TextRange> subTextRanges;
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	protected TextRangeImpl() {
+		super();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	protected EClass eStaticClass() {
+		return TexttoolsPackage.Literals.TEXT_RANGE;
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public int getOffset() {
+		return offset;
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public void setOffset(int newOffset) {
+		int oldOffset = offset;
+		offset = newOffset;
+		if (eNotificationRequired())
+			eNotify(new ENotificationImpl(this, Notification.SET, TexttoolsPackage.TEXT_RANGE__OFFSET, oldOffset, offset));
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public int getLength() {
+		return length;
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public void setLength(int newLength) {
+		int oldLength = length;
+		length = newLength;
+		if (eNotificationRequired())
+			eNotify(new ENotificationImpl(this, Notification.SET, TexttoolsPackage.TEXT_RANGE__LENGTH, oldLength, length));
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public Map<String, TextRange> getSubTextRanges() {
+		return subTextRanges;
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public void setSubTextRanges(Map<String, TextRange> newSubTextRanges) {
+		Map<String, TextRange> oldSubTextRanges = subTextRanges;
+		subTextRanges = newSubTextRanges;
+		if (eNotificationRequired())
+			eNotify(new ENotificationImpl(this, Notification.SET, TexttoolsPackage.TEXT_RANGE__SUB_TEXT_RANGES, oldSubTextRanges, subTextRanges));
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public Object eGet(int featureID, boolean resolve, boolean coreType) {
+		switch (featureID) {
+			case TexttoolsPackage.TEXT_RANGE__OFFSET:
+				return new Integer(getOffset());
+			case TexttoolsPackage.TEXT_RANGE__LENGTH:
+				return new Integer(getLength());
+			case TexttoolsPackage.TEXT_RANGE__SUB_TEXT_RANGES:
+				return getSubTextRanges();
+		}
+		return super.eGet(featureID, resolve, coreType);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public void eSet(int featureID, Object newValue) {
+		switch (featureID) {
+			case TexttoolsPackage.TEXT_RANGE__OFFSET:
+				setOffset(((Integer)newValue).intValue());
+				return;
+			case TexttoolsPackage.TEXT_RANGE__LENGTH:
+				setLength(((Integer)newValue).intValue());
+				return;
+			case TexttoolsPackage.TEXT_RANGE__SUB_TEXT_RANGES:
+				setSubTextRanges((Map<String, TextRange>)newValue);
+				return;
+		}
+		super.eSet(featureID, newValue);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public void eUnset(int featureID) {
+		switch (featureID) {
+			case TexttoolsPackage.TEXT_RANGE__OFFSET:
+				setOffset(OFFSET_EDEFAULT);
+				return;
+			case TexttoolsPackage.TEXT_RANGE__LENGTH:
+				setLength(LENGTH_EDEFAULT);
+				return;
+			case TexttoolsPackage.TEXT_RANGE__SUB_TEXT_RANGES:
+				setSubTextRanges((Map<String, TextRange>)null);
+				return;
+		}
+		super.eUnset(featureID);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public boolean eIsSet(int featureID) {
+		switch (featureID) {
+			case TexttoolsPackage.TEXT_RANGE__OFFSET:
+				return offset != OFFSET_EDEFAULT;
+			case TexttoolsPackage.TEXT_RANGE__LENGTH:
+				return length != LENGTH_EDEFAULT;
+			case TexttoolsPackage.TEXT_RANGE__SUB_TEXT_RANGES:
+				return subTextRanges != null;
+		}
+		return super.eIsSet(featureID);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public String toString() {
+		if (eIsProxy()) return super.toString();
+
+		StringBuffer result = new StringBuffer(super.toString());
+		result.append(" (offset: ");
+		result.append(offset);
+		result.append(", length: ");
+		result.append(length);
+		result.append(", subTextRanges: ");
+		result.append(subTextRanges);
+		result.append(')');
+		return result.toString();
+	}
+
+} //TextRangeImpl
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TexttoolsFactoryImpl.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TexttoolsFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..aeabcaa60ed48925b0599fe2d1136de00628d3d8
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TexttoolsFactoryImpl.java
@@ -0,0 +1,105 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools.impl;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+
+import org.eclipse.emf.ecore.impl.EFactoryImpl;
+
+import org.eclipse.emf.ecore.plugin.EcorePlugin;
+
+import org.eventb.texttools.model.texttools.*;
+
+/**
+ * <!-- begin-user-doc -->
+ * An implementation of the model <b>Factory</b>.
+ * <!-- end-user-doc -->
+ * @generated
+ */
+public class TexttoolsFactoryImpl extends EFactoryImpl implements TexttoolsFactory {
+	/**
+	 * Creates the default factory implementation.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public static TexttoolsFactory init() {
+		try {
+			TexttoolsFactory theTexttoolsFactory = (TexttoolsFactory)EPackage.Registry.INSTANCE.getEFactory("http://emf.eventb.org/models/core/texttools"); 
+			if (theTexttoolsFactory != null) {
+				return theTexttoolsFactory;
+			}
+		}
+		catch (Exception exception) {
+			EcorePlugin.INSTANCE.log(exception);
+		}
+		return new TexttoolsFactoryImpl();
+	}
+
+	/**
+	 * Creates an instance of the factory.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public TexttoolsFactoryImpl() {
+		super();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	@Override
+	public EObject create(EClass eClass) {
+		switch (eClass.getClassifierID()) {
+			case TexttoolsPackage.TEXT_RANGE: return createTextRange();
+			default:
+				throw new IllegalArgumentException("The class '" + eClass.getName() + "' is not a valid classifier");
+		}
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public TextRange createTextRange() {
+		TextRangeImpl textRange = new TextRangeImpl();
+		return textRange;
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public TexttoolsPackage getTexttoolsPackage() {
+		return (TexttoolsPackage)getEPackage();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @deprecated
+	 * @generated
+	 */
+	@Deprecated
+	public static TexttoolsPackage getPackage() {
+		return TexttoolsPackage.eINSTANCE;
+	}
+
+} //TexttoolsFactoryImpl
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TexttoolsPackageImpl.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TexttoolsPackageImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..46dee7819872c118d46a93f48caecb067523c855
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/impl/TexttoolsPackageImpl.java
@@ -0,0 +1,222 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools.impl;
+
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EGenericType;
+import org.eclipse.emf.ecore.EPackage;
+
+import org.eclipse.emf.ecore.impl.EPackageImpl;
+
+import org.eventb.texttools.model.texttools.TextRange;
+import org.eventb.texttools.model.texttools.TexttoolsFactory;
+import org.eventb.texttools.model.texttools.TexttoolsPackage;
+
+/**
+ * <!-- begin-user-doc -->
+ * An implementation of the model <b>Package</b>.
+ * <!-- end-user-doc -->
+ * @generated
+ */
+public class TexttoolsPackageImpl extends EPackageImpl implements TexttoolsPackage {
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	private EClass textRangeEClass = null;
+
+	/**
+	 * Creates an instance of the model <b>Package</b>, registered with
+	 * {@link org.eclipse.emf.ecore.EPackage.Registry EPackage.Registry} by the package
+	 * package URI value.
+	 * <p>Note: the correct way to create the package is via the static
+	 * factory method {@link #init init()}, which also performs
+	 * initialization of the package, or returns the registered package,
+	 * if one already exists.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see org.eclipse.emf.ecore.EPackage.Registry
+	 * @see org.eventb.texttools.model.texttools.TexttoolsPackage#eNS_URI
+	 * @see #init()
+	 * @generated
+	 */
+	private TexttoolsPackageImpl() {
+		super(eNS_URI, TexttoolsFactory.eINSTANCE);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	private static boolean isInited = false;
+
+	/**
+	 * Creates, registers, and initializes the <b>Package</b> for this
+	 * model, and for any others upon which it depends.  Simple
+	 * dependencies are satisfied by calling this method on all
+	 * dependent packages before doing anything else.  This method drives
+	 * initialization for interdependent packages directly, in parallel
+	 * with this package, itself.
+	 * <p>Of this package and its interdependencies, all packages which
+	 * have not yet been registered by their URI values are first created
+	 * and registered.  The packages are then initialized in two steps:
+	 * meta-model objects for all of the packages are created before any
+	 * are initialized, since one package's meta-model objects may refer to
+	 * those of another.
+	 * <p>Invocation of this method will not affect any packages that have
+	 * already been initialized.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @see #eNS_URI
+	 * @see #createPackageContents()
+	 * @see #initializePackageContents()
+	 * @generated
+	 */
+	public static TexttoolsPackage init() {
+		if (isInited) return (TexttoolsPackage)EPackage.Registry.INSTANCE.getEPackage(TexttoolsPackage.eNS_URI);
+
+		// Obtain or create and register package
+		TexttoolsPackageImpl theTexttoolsPackage = (TexttoolsPackageImpl)(EPackage.Registry.INSTANCE.getEPackage(eNS_URI) instanceof TexttoolsPackageImpl ? EPackage.Registry.INSTANCE.getEPackage(eNS_URI) : new TexttoolsPackageImpl());
+
+		isInited = true;
+
+		// Create package meta-data objects
+		theTexttoolsPackage.createPackageContents();
+
+		// Initialize created meta-data
+		theTexttoolsPackage.initializePackageContents();
+
+		// Mark meta-data to indicate it can't be changed
+		theTexttoolsPackage.freeze();
+
+		return theTexttoolsPackage;
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public EClass getTextRange() {
+		return textRangeEClass;
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public EAttribute getTextRange_Offset() {
+		return (EAttribute)textRangeEClass.getEStructuralFeatures().get(0);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public EAttribute getTextRange_Length() {
+		return (EAttribute)textRangeEClass.getEStructuralFeatures().get(1);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public EAttribute getTextRange_SubTextRanges() {
+		return (EAttribute)textRangeEClass.getEStructuralFeatures().get(2);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public TexttoolsFactory getTexttoolsFactory() {
+		return (TexttoolsFactory)getEFactoryInstance();
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	private boolean isCreated = false;
+
+	/**
+	 * Creates the meta-model objects for the package.  This method is
+	 * guarded to have no affect on any invocation but its first.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public void createPackageContents() {
+		if (isCreated) return;
+		isCreated = true;
+
+		// Create classes and their features
+		textRangeEClass = createEClass(TEXT_RANGE);
+		createEAttribute(textRangeEClass, TEXT_RANGE__OFFSET);
+		createEAttribute(textRangeEClass, TEXT_RANGE__LENGTH);
+		createEAttribute(textRangeEClass, TEXT_RANGE__SUB_TEXT_RANGES);
+	}
+
+	/**
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	private boolean isInitialized = false;
+
+	/**
+	 * Complete the initialization of the package and its meta-model.  This
+	 * method is guarded to have no affect on any invocation but its first.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public void initializePackageContents() {
+		if (isInitialized) return;
+		isInitialized = true;
+
+		// Initialize package
+		setName(eNAME);
+		setNsPrefix(eNS_PREFIX);
+		setNsURI(eNS_URI);
+
+		// Create type parameters
+
+		// Set bounds for type parameters
+
+		// Add supertypes to classes
+
+		// Initialize classes and features; add operations and parameters
+		initEClass(textRangeEClass, TextRange.class, "TextRange", !IS_ABSTRACT, !IS_INTERFACE, IS_GENERATED_INSTANCE_CLASS);
+		initEAttribute(getTextRange_Offset(), ecorePackage.getEInt(), "offset", "0", 1, 1, TextRange.class, IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
+		initEAttribute(getTextRange_Length(), ecorePackage.getEInt(), "length", "0", 1, 1, TextRange.class, IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
+		EGenericType g1 = createEGenericType(ecorePackage.getEMap());
+		EGenericType g2 = createEGenericType(ecorePackage.getEString());
+		g1.getETypeArguments().add(g2);
+		g2 = createEGenericType(this.getTextRange());
+		g1.getETypeArguments().add(g2);
+		initEAttribute(getTextRange_SubTextRanges(), g1, "subTextRanges", null, 1, 1, TextRange.class, IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_UNSETTABLE, !IS_ID, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
+
+		// Create resource
+		createResource(eNS_URI);
+	}
+
+} //TexttoolsPackageImpl
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/util/TexttoolsAdapterFactory.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/util/TexttoolsAdapterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b26fca17cc34d6400bbcebcabb532aecc2729dd
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/util/TexttoolsAdapterFactory.java
@@ -0,0 +1,131 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools.util;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notifier;
+
+import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl;
+
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.EObject;
+
+import org.eventb.texttools.model.texttools.*;
+
+/**
+ * <!-- begin-user-doc -->
+ * The <b>Adapter Factory</b> for the model.
+ * It provides an adapter <code>createXXX</code> method for each class of the model.
+ * <!-- end-user-doc -->
+ * @see org.eventb.texttools.model.texttools.TexttoolsPackage
+ * @generated
+ */
+public class TexttoolsAdapterFactory extends AdapterFactoryImpl {
+	/**
+	 * The cached model package.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	protected static TexttoolsPackage modelPackage;
+
+	/**
+	 * Creates an instance of the adapter factory.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public TexttoolsAdapterFactory() {
+		if (modelPackage == null) {
+			modelPackage = TexttoolsPackage.eINSTANCE;
+		}
+	}
+
+	/**
+	 * Returns whether this factory is applicable for the type of the object.
+	 * <!-- begin-user-doc -->
+	 * This implementation returns <code>true</code> if the object is either the model's package or is an instance object of the model.
+	 * <!-- end-user-doc -->
+	 * @return whether this factory is applicable for the type of the object.
+	 * @generated
+	 */
+	@Override
+	public boolean isFactoryForType(Object object) {
+		if (object == modelPackage) {
+			return true;
+		}
+		if (object instanceof EObject) {
+			return ((EObject)object).eClass().getEPackage() == modelPackage;
+		}
+		return false;
+	}
+
+	/**
+	 * The switch that delegates to the <code>createXXX</code> methods.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	protected TexttoolsSwitch<Adapter> modelSwitch =
+		new TexttoolsSwitch<Adapter>() {
+			@Override
+			public Adapter caseTextRange(TextRange object) {
+				return createTextRangeAdapter();
+			}
+			@Override
+			public Adapter defaultCase(EObject object) {
+				return createEObjectAdapter();
+			}
+		};
+
+	/**
+	 * Creates an adapter for the <code>target</code>.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @param target the object to adapt.
+	 * @return the adapter for the <code>target</code>.
+	 * @generated
+	 */
+	@Override
+	public Adapter createAdapter(Notifier target) {
+		return modelSwitch.doSwitch((EObject)target);
+	}
+
+
+	/**
+	 * Creates a new adapter for an object of class '{@link org.eventb.texttools.model.texttools.TextRange <em>Text Range</em>}'.
+	 * <!-- begin-user-doc -->
+	 * This default implementation returns null so that we can easily ignore cases;
+	 * it's useful to ignore a case when inheritance will catch all the cases anyway.
+	 * <!-- end-user-doc -->
+	 * @return the new adapter.
+	 * @see org.eventb.texttools.model.texttools.TextRange
+	 * @generated
+	 */
+	public Adapter createTextRangeAdapter() {
+		return null;
+	}
+
+	/**
+	 * Creates a new adapter for the default case.
+	 * <!-- begin-user-doc -->
+	 * This default implementation returns null.
+	 * <!-- end-user-doc -->
+	 * @return the new adapter.
+	 * @generated
+	 */
+	public Adapter createEObjectAdapter() {
+		return null;
+	}
+
+} //TexttoolsAdapterFactory
diff --git a/org.eventb.texttools/src/org/eventb/texttools/model/texttools/util/TexttoolsSwitch.java b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/util/TexttoolsSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2acbaecf67acd02b32577282ad41db3cb732d85
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/model/texttools/util/TexttoolsSwitch.java
@@ -0,0 +1,137 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+/**
+ * <copyright>
+ * </copyright>
+ *
+ * $Id$
+ */
+package org.eventb.texttools.model.texttools.util;
+
+import java.util.List;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EModelElement;
+import org.eclipse.emf.ecore.EObject;
+
+import org.eventb.texttools.model.texttools.*;
+
+/**
+ * <!-- begin-user-doc -->
+ * The <b>Switch</b> for the model's inheritance hierarchy.
+ * It supports the call {@link #doSwitch(EObject) doSwitch(object)}
+ * to invoke the <code>caseXXX</code> method for each class of the model,
+ * starting with the actual class of the object
+ * and proceeding up the inheritance hierarchy
+ * until a non-null result is returned,
+ * which is the result of the switch.
+ * <!-- end-user-doc -->
+ * @see org.eventb.texttools.model.texttools.TexttoolsPackage
+ * @generated
+ */
+public class TexttoolsSwitch<T> {
+	/**
+	 * The cached model package
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	protected static TexttoolsPackage modelPackage;
+
+	/**
+	 * Creates an instance of the switch.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @generated
+	 */
+	public TexttoolsSwitch() {
+		if (modelPackage == null) {
+			modelPackage = TexttoolsPackage.eINSTANCE;
+		}
+	}
+
+	/**
+	 * Calls <code>caseXXX</code> for each class of the model until one returns a non null result; it yields that result.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the first non-null result returned by a <code>caseXXX</code> call.
+	 * @generated
+	 */
+	public T doSwitch(EObject theEObject) {
+		return doSwitch(theEObject.eClass(), theEObject);
+	}
+
+	/**
+	 * Calls <code>caseXXX</code> for each class of the model until one returns a non null result; it yields that result.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the first non-null result returned by a <code>caseXXX</code> call.
+	 * @generated
+	 */
+	protected T doSwitch(EClass theEClass, EObject theEObject) {
+		if (theEClass.eContainer() == modelPackage) {
+			return doSwitch(theEClass.getClassifierID(), theEObject);
+		}
+		else {
+			List<EClass> eSuperTypes = theEClass.getESuperTypes();
+			return
+				eSuperTypes.isEmpty() ?
+					defaultCase(theEObject) :
+					doSwitch(eSuperTypes.get(0), theEObject);
+		}
+	}
+
+	/**
+	 * Calls <code>caseXXX</code> for each class of the model until one returns a non null result; it yields that result.
+	 * <!-- begin-user-doc -->
+	 * <!-- end-user-doc -->
+	 * @return the first non-null result returned by a <code>caseXXX</code> call.
+	 * @generated
+	 */
+	protected T doSwitch(int classifierID, EObject theEObject) {
+		switch (classifierID) {
+			case TexttoolsPackage.TEXT_RANGE: {
+				TextRange textRange = (TextRange)theEObject;
+				T result = caseTextRange(textRange);
+				if (result == null) result = defaultCase(theEObject);
+				return result;
+			}
+			default: return defaultCase(theEObject);
+		}
+	}
+
+	/**
+	 * Returns the result of interpreting the object as an instance of '<em>Text Range</em>'.
+	 * <!-- begin-user-doc -->
+	 * This implementation returns null;
+	 * returning a non-null result will terminate the switch.
+	 * <!-- end-user-doc -->
+	 * @param object the target of the switch.
+	 * @return the result of interpreting the object as an instance of '<em>Text Range</em>'.
+	 * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject)
+	 * @generated
+	 */
+	public T caseTextRange(TextRange object) {
+		return null;
+	}
+
+	/**
+	 * Returns the result of interpreting the object as an instance of '<em>EObject</em>'.
+	 * <!-- begin-user-doc -->
+	 * This implementation returns null;
+	 * returning a non-null result will terminate the switch, but this is the last case anyway.
+	 * <!-- end-user-doc -->
+	 * @param object the target of the switch.
+	 * @return the result of interpreting the object as an instance of '<em>EObject</em>'.
+	 * @see #doSwitch(org.eclipse.emf.ecore.EObject)
+	 * @generated
+	 */
+	public T defaultCase(EObject object) {
+		return null;
+	}
+
+} //TexttoolsSwitch
diff --git a/org.eventb.texttools/src/org/eventb/texttools/prettyprint/ContextPrintSwitch.java b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/ContextPrintSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..099449082ca670547991a08c33d434e9983e5515
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/ContextPrintSwitch.java
@@ -0,0 +1,92 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.prettyprint;
+
+import org.eclipse.emf.common.util.EList;
+import org.eventb.emf.core.context.Axiom;
+import org.eventb.emf.core.context.Context;
+import org.eventb.emf.core.context.util.ContextSwitch;
+import org.eventb.texttools.Constants;
+
+/**
+ * {@link ContextSwitch} which supports {@link PrettyPrinter}s. This class is
+ * not intended to be used by clients other than the {@link PrettyPrinter}.
+ */
+public class ContextPrintSwitch extends ContextSwitch<Boolean> implements
+		PrettyPrintConstants, Constants {
+
+	private final PrettyPrinter printer;
+
+	protected ContextPrintSwitch(final PrettyPrinter prettyPrinter) {
+		printer = prettyPrinter;
+	}
+
+	@Override
+	public Boolean caseContext(final Context object) {
+		/*
+		 * Header for this context
+		 */
+		printer.appendWithSpace(CONTEXT);
+		printer.append(object.getName());
+		printer.appendComment(object);
+
+		final EList<String> extendsNames = object.getExtendsNames();
+		if (extendsNames.size() > 0) {
+			printer.append(SPACE);
+			printer.appendWithSpace(EXTENDS);
+			printer.appendStringList(extendsNames);
+		}
+
+		printer.appendLineBreak();
+
+		/*
+		 * Now all context clauses
+		 */
+		final boolean newLine = (Boolean) printer.getPreference(
+				PROP_NEWLINE_BETWEEN_CLAUSES, true);
+
+		// constants
+		printer.appendNameList(object.getConstants(), CONSTANTS, newLine);
+
+		// sets
+		printer.appendNameList(object.getSets(), SETS, newLine);
+
+		// axioms
+		printAxioms(object.getAxioms(), newLine);
+
+		/*
+		 * context footer
+		 */
+		printer.adjustIndent();
+		printer.appendWithLineBreak(END);
+
+		return true;
+	}
+
+	@Override
+	public Boolean caseAxiom(final Axiom object) {
+		printer.appendLabeledPredicate(object, true);
+		return true;
+	}
+
+	private void printAxioms(final EList<Axiom> axioms, final boolean newLine) {
+		if (axioms.size() > 0) {
+			if (newLine) {
+				printer.appendLineBreak();
+			}
+
+			printer.appendWithLineBreak(AXIOMS);
+			printer.increaseIndentLevel();
+
+			for (final Axiom axiom : axioms) {
+				doSwitch(axiom);
+			}
+
+			printer.decreaseIndentLevel();
+		}
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/prettyprint/MachinePrintSwitch.java b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/MachinePrintSwitch.java
new file mode 100644
index 0000000000000000000000000000000000000000..99eb64f8e65bd2e6b18364e9b665f8788df1aa18
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/MachinePrintSwitch.java
@@ -0,0 +1,286 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.prettyprint;
+
+import java.util.Iterator;
+
+import org.eclipse.emf.common.util.EList;
+import org.eventb.emf.core.machine.Action;
+import org.eventb.emf.core.machine.Convergence;
+import org.eventb.emf.core.machine.Event;
+import org.eventb.emf.core.machine.Guard;
+import org.eventb.emf.core.machine.Invariant;
+import org.eventb.emf.core.machine.Machine;
+import org.eventb.emf.core.machine.Variant;
+import org.eventb.emf.core.machine.Witness;
+import org.eventb.emf.core.machine.util.MachineSwitch;
+import org.eventb.texttools.Constants;
+
+/**
+ * {@link MachineSwitch} which supports {@link PrettyPrinter}s. This class is
+ * not intended to be used by clients other than the {@link PrettyPrinter}.
+ */
+public class MachinePrintSwitch extends MachineSwitch<Boolean> implements
+		PrettyPrintConstants, Constants {
+
+	private final PrettyPrinter printer;
+
+	protected MachinePrintSwitch(final PrettyPrinter prettyPrinter) {
+		printer = prettyPrinter;
+	}
+
+	@Override
+	public Boolean caseMachine(final Machine object) {
+		/*
+		 * Header for this machine
+		 */
+		printer.appendWithSpace(MACHINE);
+		printer.append(object.getName());
+		printer.appendComment(object);
+
+		final EList<String> refinesNames = object.getRefinesNames();
+		if (refinesNames.size() > 0) {
+			printer.append(SPACE);
+			printer.appendWithSpace(REFINES);
+			printer.appendStringList(refinesNames);
+		}
+
+		final EList<String> seesNames = object.getSeesNames();
+		if (seesNames.size() > 0) {
+			printer.append(SPACE);
+			printer.appendWithSpace(SEES);
+			printer.appendStringList(seesNames);
+		}
+
+		printer.appendLineBreak();
+
+		/*
+		 * Now all machine clauses
+		 */
+		final boolean newLine = (Boolean) printer.getPreference(
+				PROP_NEWLINE_BETWEEN_CLAUSES, true);
+
+		// variables
+		printer.appendNameList(object.getVariables(), VARIABLES, newLine);
+
+		// invariants
+		printInvariants(object.getInvariants(), newLine);
+
+		// variant
+		final Variant variant = object.getVariant();
+		if (variant != null) {
+			if (newLine) {
+				printer.appendLineBreak();
+			}
+			doSwitch(variant);
+		}
+
+		// events
+		printEvents(object.getEvents(), newLine);
+
+		/*
+		 * machine footer
+		 */
+		printer.adjustIndent();
+		printer.appendWithLineBreak(END);
+
+		return true;
+	}
+
+	@Override
+	public Boolean caseInvariant(final Invariant object) {
+		printer.appendLabeledPredicate(object, true);
+		return true;
+	}
+
+	@Override
+	public Boolean caseGuard(final Guard object) {
+		printer.appendLabeledPredicate(object, false);
+		return true;
+	}
+
+	@Override
+	public Boolean caseWitness(final Witness object) {
+		printer.appendLabeledPredicate(object, false);
+		return true;
+	}
+
+	@Override
+	public Boolean caseAction(final Action object) {
+		printer.adjustIndent();
+		printer.appendLabel(object);
+		printer.appendFormula(object.getAction(), printer.hasComment(object));
+		printer.appendComment(object);
+
+		return true;
+	}
+
+	@Override
+	public Boolean caseVariant(final Variant object) {
+		printer.appendWithSpace(VARIANT);
+		printer.appendFormula(object.getExpression(), printer
+				.hasComment(object));
+		printer.appendComment(object);
+
+		return true;
+	}
+
+	@Override
+	public Boolean caseEvent(final Event object) {
+		/*
+		 * event header
+		 */
+		printer.adjustIndent();
+
+		// convergence
+		final Convergence convergence = object.getConvergence();
+		if (Convergence.CONVERGENT.equals(convergence)
+				&& (Boolean) printer.getPreference(PROP_PRINT_ORDINARY_KEYWORD,
+						false)) {
+			printer.appendWithSpace(ORDINARY);
+		} else if (Convergence.ANTICIPATED.equals(convergence)) {
+			printer.appendWithSpace(ANTICIPATED);
+		} else if (Convergence.CONVERGENT.equals(convergence)) {
+			printer.appendWithSpace(CONVERGENT);
+		}
+
+		// 'event' + name
+		printer.appendWithSpace(EVENT);
+		printer.append(object.getName());
+		printer.appendComment(object);
+
+		// refines / extends
+		final EList<String> refinesNames = object.getRefinesNames();
+		if (refinesNames.size() > 0) {
+			if (printer.hasComment(object)) {
+				printer.adjustIndent();
+			} else {
+				printer.append(SPACE);
+			}
+
+			if (object.isExtended()) {
+				printer.appendWithSpace(EXTENDS);
+			} else {
+				printer.appendWithSpace(REFINES);
+			}
+
+			printer.appendStringList(refinesNames);
+			printer.appendLineBreak();
+		}
+
+		if (!printer.hasComment(object) && refinesNames.size() == 0) {
+			printer.appendLineBreak();
+		}
+
+		/*
+		 * event body
+		 */
+		printer.increaseIndentLevel();
+
+		// parameters
+		printer.appendNameList(object.getParameters(), ANY, false);
+
+		// guards
+		final EList<Guard> guards = object.getGuards();
+		if (guards.size() > 0) {
+			printer.adjustIndent();
+			printer.appendWithLineBreak(WHERE);
+
+			printer.increaseIndentLevel();
+			for (final Guard guard : guards) {
+				doSwitch(guard);
+			}
+			printer.decreaseIndentLevel();
+		}
+
+		// witnesses
+		final EList<Witness> witnesses = object.getWitnesses();
+		if (witnesses.size() > 0) {
+			printer.adjustIndent();
+			printer.appendWithLineBreak(WITH);
+
+			printer.increaseIndentLevel();
+			for (final Witness witness : witnesses) {
+				doSwitch(witness);
+			}
+			printer.decreaseIndentLevel();
+		}
+
+		// actions
+		final EList<Action> actions = object.getActions();
+		if (actions.size() > 0) {
+			printer.adjustIndent();
+			printer.appendWithLineBreak(THEN);
+
+			printer.increaseIndentLevel();
+			for (final Action action : actions) {
+				doSwitch(action);
+			}
+			printer.decreaseIndentLevel();
+		}
+
+		/*
+		 * event footer
+		 */
+		printer.decreaseIndentLevel();
+		printer.adjustIndent();
+		printer.appendWithLineBreak(END);
+
+		return true;
+	}
+
+	private void printInvariants(final EList<Invariant> invariants,
+			final boolean newLine) {
+		if (invariants.size() > 0) {
+			if (newLine) {
+				printer.appendLineBreak();
+			}
+
+			printer.appendWithLineBreak(INVARIANTS);
+			printer.increaseIndentLevel();
+
+			for (final Invariant invariant : invariants) {
+				doSwitch(invariant);
+			}
+
+			printer.decreaseIndentLevel();
+		}
+	}
+
+	private void printEvents(final EList<Event> events, final boolean newLine) {
+		if (events.size() > 0) {
+			if (newLine) {
+				printer.appendLineBreak();
+			}
+
+			printer.appendWithLineBreak(EVENTS);
+
+			final Boolean indentEvents = (Boolean) printer.getPreference(
+					PROP_INDENT_EVENTS, true);
+			final Boolean linebreakBetweenEvents = (Boolean) printer
+					.getPreference(PROP_NEWLINE_BETWEEN_EVENTS, true);
+
+			if (indentEvents) {
+				printer.increaseIndentLevel();
+			}
+
+			for (final Iterator<Event> iterator = events.iterator(); iterator
+					.hasNext();) {
+				final Event event = iterator.next();
+				doSwitch(event);
+
+				if (linebreakBetweenEvents && iterator.hasNext()) {
+					printer.appendLineBreak();
+				}
+			}
+
+			if (indentEvents) {
+				printer.decreaseIndentLevel();
+			}
+		}
+	}
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/prettyprint/PrettyPrintConstants.java b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/PrettyPrintConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..75f7cb10d30bfafae0a2ef8d91b0381df08ac0d2
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/PrettyPrintConstants.java
@@ -0,0 +1,24 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.prettyprint;
+
+public interface PrettyPrintConstants {
+	public static final String PROP_INDENT_DEPTH = "indentation.depth";
+	public static final String PROP_USE_TABS_FOR_INDENTATION = "use.tabs.for.indentation";
+	public static final String PROP_TAB_WIDTH = "tab.width";
+	public static final String PROP_INDENT_EVENTS = "indent.events";
+	public static final String PROP_NEWLINE_BETWEEN_CLAUSES = "newline.between.clauses";
+	public static final String PROP_NEWLINE_BETWEEN_EVENTS = "newline.between.events";
+	public static final String PROP_PRINT_ORDINARY_KEYWORD = "print.ordinary.keyword";
+
+	public static final char SPACE = ' ';
+	public static final char TAB = '\t';
+	public static final char AT = '@';
+	public static final String COMMENT_SINGLELINE_BEGIN = "// ";
+	public static final String COMMENT_MULTILINE_BEGIN = "/* ";
+	public static final String COMMENT_MULTILINE_END = " */";
+}
diff --git a/org.eventb.texttools/src/org/eventb/texttools/prettyprint/PrettyPrinter.java b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/PrettyPrinter.java
new file mode 100644
index 0000000000000000000000000000000000000000..e656b8c4a05544b93ed7188dd408de34757024aa
--- /dev/null
+++ b/org.eventb.texttools/src/org/eventb/texttools/prettyprint/PrettyPrinter.java
@@ -0,0 +1,360 @@
+/** 
+ * (c) 2009 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, 
+ * Heinrich Heine Universitaet Duesseldorf
+ * This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) 
+ * */
+
+package org.eventb.texttools.prettyprint;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.emf.common.util.EList;
+import org.eventb.emf.core.EventBCommented;
+import org.eventb.emf.core.EventBDerived;
+import org.eventb.emf.core.EventBNamed;
+import org.eventb.emf.core.EventBNamedCommentedPredicateElement;
+import org.eventb.emf.core.EventBObject;
+import org.eventb.emf.core.context.ContextPackage;
+import org.eventb.emf.core.machine.MachinePackage;
+import org.eventb.texttools.Constants;
+
+public class PrettyPrinter implements PrettyPrintConstants {
+	protected final String linebreak;
+
+	private final Map<String, Object> preferences;
+	private final MachinePrintSwitch machineSwitch;
+	private final ContextPrintSwitch contextSwitch;
+
+	private final StringBuilder buffer;
+	private int indentationLevel = 0;
+
+	private int indentWidth;
+	private int tabWidth;
+	private Boolean useTabsForIndentation;
+
+	/**
+	 * Creates a new pretty printer with the given configuration.
+	 *
+	 * @param buffer
+	 *            {@link StringBuilder} to which this pretty printer append its
+	 *            results.
+	 * @param linebreak
+	 *            The linebreak character to be used.
+	 * @param preferences
+	 *            A {@link Map<String, Object>} of preferences which shall be
+	 *            used while prettyprinting. Possible keys are available as
+	 *            constants in the {@link PrettyPrintConstants}.
+	 */
+	public PrettyPrinter(final StringBuilder buffer, final String linebreak,
+			final Map<String, Object> preferences) {
+		this.buffer = buffer;
+		this.linebreak = linebreak;
+
+		if (preferences != null) {
+			this.preferences = preferences;
+		} else {
+			this.preferences = new HashMap<String, Object>();
+		}
+
+		machineSwitch = new MachinePrintSwitch(this);
+		contextSwitch = new ContextPrintSwitch(this);
+
+		initPreferences();
+	}
+
+	private void initPreferences() {
+		indentWidth = (Integer) getPreference(PROP_INDENT_DEPTH, 2);
+		useTabsForIndentation = (Boolean) getPreference(
+				PROP_USE_TABS_FOR_INDENTATION, false);
+		tabWidth = (Integer) getPreference(PROP_TAB_WIDTH, 4);
+	}
+
+	/**
+	 * Creates a pretty print for the given {@link EventBObject} and appends it
+	 * to the {@link StringBuilder} which was handed to this
+	 * {@link PrettyPrinter} instance via the constructor.
+	 *
+	 * @param object
+	 */
+	public void prettyPrint(final EventBObject emfObject) {
+		final String packageNsURI = emfObject.eClass().getEPackage().getNsURI();
+
+		if (packageNsURI.equals(MachinePackage.eNS_URI)) {
+			machineSwitch.doSwitch(emfObject);
+		} else if (packageNsURI.equals(ContextPackage.eNS_URI)) {
+			contextSwitch.doSwitch(emfObject);
+		}
+	}
+
+	protected Object getPreference(final String key, final Object defaultValue) {
+		if (preferences.containsKey(key)) {
+			return preferences.get(key);
+		} else {
+			return defaultValue;
+		}
+	}
+
+	protected void changeIndent(final int byValue) {
+		Assert.isTrue(indentationLevel + byValue >= 0);
+		indentationLevel += byValue;
+	}
+
+	protected void increaseIndentLevel() {
+		changeIndent(indentWidth);
+	}
+
+	protected void decreaseIndentLevel() {
+		changeIndent(-indentWidth);
+	}
+
+	protected void append(final char c) {
+		buffer.append(c);
+	}
+
+	protected void append(final String string) {
+		buffer.append(string);
+	}
+
+	protected void appendWithSpace(final String string) {
+		buffer.append(string);
+		buffer.append(SPACE);
+	}
+
+	protected void appendWithSpace(final char c) {
+		buffer.append(c);
+		buffer.append(SPACE);
+	}
+
+	protected void appendWithLineBreak(final String string) {
+		buffer.append(string);
+		appendLineBreak();
+	}
+
+	protected void appendLineBreak() {
+		buffer.append(linebreak);
+	}
+
+	/**
+	 * Appends the comment of the given {@link EventBCommented} if it contains a
+	 * comment.
+	 *
+	 * @param commentedElement
+	 * @return <code>true</code> if it contained a comment and this comment
+	 *         consisted of multiple lines, <code>false</code> otherwise
+	 */
+	protected void appendComment(final EventBCommented commentedElement) {
+		final String comment = commentedElement.getComment();
+
+		if (!hasComment(commentedElement)) {
+			return;
+		}
+
+		// insert blank if previous char is not a whitespace
+		if (buffer.length() > 0
+				&& !Character.isWhitespace(buffer.charAt(buffer.length() - 1))) {
+			append(SPACE);
+		}
+
+		final StringTokenizer tokenizer = new StringTokenizer(comment, "\n\r");
+
+		if (tokenizer.countTokens() <= 1) {
+			append(COMMENT_SINGLELINE_BEGIN);
+			append(comment.trim());
+			appendLineBreak();
+		}
+		// multi line comment
+		else {
+			appendLineBreak();
+			adjustIndent();
+			append(COMMENT_MULTILINE_BEGIN);
+
+			final int subIdentation = getCurrentIndentation()
+					- indentationLevel - 1;
+
+			append(tokenizer.nextToken().trim());
+			appendLineBreak();
+
+			changeIndent(subIdentation);
+
+			while (tokenizer.hasMoreTokens()) {
+				adjustIndent();
+				append(tokenizer.nextToken().trim());
+
+				if (tokenizer.hasMoreTokens()) {
+					appendLineBreak();
+				}
+			}
+
+			append(COMMENT_MULTILINE_END);
+			appendLineBreak();
+
+			changeIndent(-subIdentation);
+		}
+	}
+
+	protected boolean hasComment(final EventBCommented commentedElement) {
+		final String comment = commentedElement.getComment();
+		return comment != null && comment.length() > 0;
+	}
+
+	protected void ensureNewLine() {
+		final int lastLinebreak = buffer.lastIndexOf(linebreak);
+
+		/*
+		 * Check if: 1) last line has characters and 2) these characters are not
+		 * all whitespaces
+		 */
+		if (lastLinebreak < buffer.length() - 1
+				&& buffer.substring(lastLinebreak, buffer.length() - 1).trim()
+						.length() > 0) {
+			// begin new line
+			appendLineBreak();
+		}
+	}
+
+	protected void adjustIndent() {
+		int indentRemaining = indentationLevel - getCurrentIndentation() + 1;
+
+		if (useTabsForIndentation) {
+			while (indentRemaining >= tabWidth) {
+				append(TAB);
+				indentRemaining -= tabWidth;
+			}
+		}
+
+		// then use spaces for rest
+		while (indentRemaining > 0) {
+			append(SPACE);
+			indentRemaining--;
+		}
+	}
+
+	protected void appendLabeledPredicate(final EventBNamedCommentedPredicateElement object,
+			final boolean derivedPossible) {
+		adjustIndent();
+
+		// deal with derived predicates (theorems)
+		if (object instanceof EventBDerived
+				&& ((EventBDerived) object).isTheorem()) {
+			appendWithSpace(Constants.THEOREM);
+		}
+
+		appendLabel(object);
+		appendFormula(object.getPredicate(), hasComment(object));
+		appendComment(object);
+	}
+
+	/**
+	 * Returns the indentation level of the current position, i.e., how many
+	 * characters the buffer contains behind the last line break.
+	 *
+	 * @return
+	 */
+	private int getCurrentIndentation() {
+		final int lastLinebreak = buffer.lastIndexOf(linebreak);
+		return buffer.length() - lastLinebreak;
+	}
+
+	protected void appendFormula(final String formula,
+			final boolean followedByComment) {
+		final StringTokenizer tokenizer = new StringTokenizer(formula, "\n\r");
+
+		if (tokenizer.countTokens() <= 1) {
+			if (!followedByComment) {
+				appendWithLineBreak(formula.trim());
+			} else {
+				append(formula.trim());
+			}
+		} else {
+			final int subIndent = getCurrentIndentation() - indentationLevel
+					- 1;
+
+			appendWithLineBreak(tokenizer.nextToken().trim());
+			changeIndent(subIndent);
+
+			while (tokenizer.hasMoreTokens()) {
+				adjustIndent();
+				final String line = tokenizer.nextToken().trim();
+
+				if (tokenizer.hasMoreTokens() || !followedByComment) {
+					appendWithLineBreak(line);
+				} else {
+					append(line);
+				}
+			}
+
+			changeIndent(-subIndent);
+		}
+
+	}
+
+	protected void appendLabel(final EventBNamed namedElement) {
+		final String name = namedElement.getName();
+		append(AT);
+		appendWithSpace(name);
+	}
+
+	protected void appendStringList(final List<String> strings) {
+		for (int i = 0; i < strings.size(); i++) {
+			appendWithSpace(strings.get(i));
+		}
+	}
+
+	protected void appendNameList(final EList<? extends EventBNamed> list,
+			final String label, final boolean newLineBefore) {
+		if (list.size() > 0) {
+			if (newLineBefore) {
+				appendLineBreak();
+			}
+
+			adjustIndent();
+			appendWithSpace(label);
+			appendNamedElementList(list);
+		}
+	}
+
+	protected void appendNamedElementList(
+			final EList<? extends EventBNamed> elements) {
+		final int subIndent = getCurrentIndentation() - indentationLevel - 1;
+		changeIndent(subIndent);
+
+		boolean lastBeganNewLine = true;
+
+		for (int i = 0; i < elements.size(); i++) {
+			final EventBNamed element = elements.get(i);
+			EventBCommented commented = null;
+
+			if (element instanceof EventBCommented) {
+				commented = (EventBCommented) element;
+				commented = hasComment(commented) ? commented : null;
+
+				if (commented != null && !lastBeganNewLine) {
+					appendLineBreak();
+				}
+			}
+
+			adjustIndent();
+			append(element.getName());
+
+			// begin a new line if we had a comment for this element
+			if (commented != null) {
+				appendComment(commented);
+				lastBeganNewLine = true;
+
+				if (i < elements.size() - 1) {
+					adjustIndent();
+				}
+			} else {
+				append(SPACE);
+				lastBeganNewLine = false;
+			}
+		}
+
+		appendLineBreak();
+		changeIndent(-subIndent);
+	}
+}