blob: 34e4b0c076b840beb8118629bcab7675b2a78f04 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Xtext - Five simple steps to your JVM language</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description"
content="The website of Eclipse Xtext, an open-source framework for development of programming languages and domain-specific languages">
<meta name="author" content="Sven Efftinge">
<meta name="author" content="Miro Spoenemann">
<!-- styles -->
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Le fav and touch icons -->
<link rel="shortcut icon" href="/Xtext/images/favicon.png">
<link href="/Xtext/css/bootstrap.css" rel="stylesheet" type='text/css'>
<link href="/Xtext/css/bootstrap-responsive.css" rel="stylesheet" type='text/css'>
<link href="/Xtext/css/shield-responsive.css" rel="stylesheet" type='text/css'>
<link href='/Xtext/css/fonts.css' rel='stylesheet' type='text/css'>
<link href="/Xtext/css/prettyPhoto.css" rel="stylesheet" media="screen" type='text/css'>
<link href="/Xtext/css/prettify.css" type="text/css" rel="stylesheet"/>
<link href="/Xtext/css/style.css" rel="stylesheet" type='text/css'>
<!-- cover flow -->
<link href="/Xtext/css/coverflow.css" rel="stylesheet" type='text/css'>
<!--[if lt IE 9]>
<link href="/css/iebugs.css" rel="stylesheet" type='text/css'>
<![endif]-->
<!-- BEGIN Cookie Consent
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.js"></script>
<script>
window.addEventListener("load", function(){
window.cookieconsent.initialise({
"palette": {
"popup": {
"background": "#000"
},
"button": {
"background": "#f1d600"
}
},
"theme": "edgeless",
"type": "opt-in",
onInitialise: function (status) {
var type = this.options.type;
var didConsent = this.hasConsented();
if (type == 'opt-in' && didConsent) {
// TODO: enable cookies
}
if (type == 'opt-out' && !didConsent) {
// TODO: disable cookies
}
},
onStatusChange: function(status, chosenBefore) {
var type = this.options.type;
var didConsent = this.hasConsented();
if (type == 'opt-in' && didConsent) {
// TODO: enable cookies
}
if (type == 'opt-out' && !didConsent) {
// TODO: disable cookies
}
},
onRevokeChoice: function() {
var type = this.options.type;
if (type == 'opt-in') {
// TODO: disable cookies
}
if (type == 'opt-out') {
// TODO: enable cookies
}
},
"content": {
"href": "http://www.eclipse.org/legal/privacy.php"
}
})});
</script>
END Cookie Consent -->
</head>
<body>
<header class="site-header">
<!-- Navbar -->
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse"
data-target=".nav-collapse"> <span class="icon-bar"></span> <span
class="icon-bar"></span> <span class="icon-bar"></span>
</a> <a class="brand" href="/Xtext/index.html"></a>
<div class="nav-collapse collapse" style="height: 0px;">
<ul class="nav">
<!--li ><a href="/Xtext/news.html">News</a></li-->
<li ><a href="/Xtext/download.html">Download</a></li>
<li ><a href="/Xtext/documentation/index.html">Documentation</a></li>
<li ><a href="/Xtext/community.html">Community</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Support &amp; Trainings<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="https://www.itemis.com/en/xtext/support-and-team/" target="_blank">itemis</a></li>
<li><a href="https://www.typefox.io/language-engineering/" target="_blank">TypeFox</a></li>
</ul>
</li>
<li ><a href="http://xtend-lang.org">Xtend</a></li>
</ul>
<!--div class="nav pull-right">
<li ><a><iframe src="https://ghbtns.com/github-btn.html?user=eclipse&repo=xtext&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe></a></li>
</div-->
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<!-- Navbar End -->
</header>
<div class="page-content">
<script>
function startSearch(event) {
if (event.keyCode == 13) {
var q = 'site:eclipse.org/Xtext/documentation+' + event.target.value;
window.open('https://www.google.com/search?q=' + q, "_self");
}
}
</script>
<div class="wrapper">
<div id="page">
<div class="inner">
<div id="maincontainer" class="container">
<span class="edit-on-github pull-right">
<a href="https://github.com/eclipse/xtext/edit/website-published/xtext-website/documentation/104_jvmdomainmodel.md">Edit on Github</a>
</span>
<div class="span3" style="margin-left: 0px;">
<div class="search-bar">
<img src="/Xtext/images/search-gray.png"/>
<input type="search" id="google-search" onkeyup="startSearch(event);"/>
</div>
<ul id="nav-outline" style="margin-left: 0px;">
<li class="nav-part">Getting Started</li>
<li><a href="102_domainmodelwalkthrough.html">15 Minutes Tutorial</a></li>
<li><a href="103_domainmodelnextsteps.html">15 Minutes Tutorial - Extended</a></li>
<li><a href="104_jvmdomainmodel.html">Five simple steps to your JVM language</a></li>
<li class="nav-part">Reference Documentation</li>
<li><a href="301_grammarlanguage.html">The Grammar Language</a></li>
<li><a href="302_configuration.html">Configuration</a></li>
<li><a href="303_runtime_concepts.html">Language Implementation</a></li>
<li><a href="305_xbase.html">Integration with Java</a></li>
<li><a href="307_special_languages.html">Typical Language Configurations</a></li>
<li><a href="308_emf_integration.html">Integration with EMF</a></li>
<li><a href="310_eclipse_support.html">Eclipse Support</a></li>
<li><a href="330_web_support.html">Web Editor Support</a></li>
<li><a href="340_lsp_support.html">LSP Support</a></li>
<li><a href="350_continuous_integration.html">Continuous Integration</a></li>
</ul>
</div>
<div class="span8 doc-contents">
<h1 id="jvm-domain-model">Five simple steps to your JVM language</h1>
<p>In this tutorial we will basically implement the <a href="https://www.eclipse.org/Xtext/documentation/103_domainmodelnextsteps.html">domain model language</a> again, but this time we will make use of the special JVM support shipped with Xtext. This kind of language is really a sweet spot for DSLs, so feel free to use this as a blueprint and add your project specific features later on.</p>
<p>The revised domain model language supports expressions and cross-links to Java types. It is directly translated to Java source code. The syntax should look very familiar. Here is an example:</p>
<pre><code class="language-domainexample">import java.util.List
package my.model {
entity Person {
name: String
firstName: String
friends: List&lt;Person&gt;
address : Address
op getFullName() : String {
return firstName + " " + name;
}
op getFriendsSortedByFullName() : List&lt;Person&gt; {
return friends.sortBy[ f | f.fullName ]
}
}
entity Address {
street: String
zip: String
city: String
}
}
</code></pre>
<p>As you can see, it supports all kinds of advanced features such as Java generics and full expressions, even including lambda expressions. Don’t panic, you will not have to implement these concepts on your own but will reuse a lot of helpful infrastructure to build the language.</p>
<p>We will now walk through the five little steps needed to get this language fully working including its compiler. The first three steps are very similar to those in the <a href="102_domainmodelwalkthrough.html">15 Minutes Tutorial</a>.</p>
<p>After you have installed Xtext on your machine, start Eclipse and set up a fresh workspace.</p>
<h2 id="domain-model-step1">Step One: Create A New Xtext Project</h2>
<p>In order to get started we first need to create some Eclipse projects. Use the Eclipse wizard to do so:</p>
<p><em>File → New → Project… → Xtext → Xtext Project</em></p>
<p>Choose a meaningful project name, language name and file extension, e.g.</p>
<table>
<tbody>
<tr>
<td><strong>Project name:</strong></td>
<td>org.example.domainmodel</td>
</tr>
<tr>
<td><strong>Language name:</strong></td>
<td>org.example.domainmodel.Domainmodel</td>
</tr>
<tr>
<td><strong>File extension:</strong></td>
<td>dmodel</td>
</tr>
</tbody>
</table>
<p>Click on <em>Finish</em> to create the projects</p>
<p><img src="images/30min_wizard.png" alt="" /></p>
<p>After you have successfully finished the wizard, you will find five new projects in your workspace.</p>
<table>
<tbody>
<tr>
<td>org.example.domainmodel</td>
<td>The grammar definition and all language-specific components (parser, lexer, linker, validation, etc.)</td>
</tr>
<tr>
<td>org.example.domainmodel.tests</td>
<td>Unit tests for the language</td>
</tr>
<tr>
<td>org.example.domainmodel.ide</td>
<td>Platform-independent IDE functionality (e.g. services for content assist)</td>
</tr>
<tr>
<td>org.example.domainmodel.ui</td>
<td>The Eclipse editor and other workbench related functionality</td>
</tr>
<tr>
<td>org.example.domainmodel.ui.tests</td>
<td>Unit tests for the Eclipse editor</td>
</tr>
</tbody>
</table>
<p><img src="images/30min_initialprojectlayout.png" alt="" /></p>
<h2 id="domain-model-step2">Step Two: Write the Grammar</h2>
<p>The wizard will automatically open the grammar file <em>Domainmodel.xtext</em> in the editor. As you can see it already contains a simple <em>Hello World</em> grammar. Replace that grammar definition with the one for our language:</p>
<pre><code class="language-xtext">grammar org.example.domainmodel.Domainmodel with
org.eclipse.xtext.xbase.Xbase
generate domainmodel "http://www.example.org/domainmodel/Domainmodel"
Domainmodel:
importSection=XImportSection?
elements+=AbstractElement*;
AbstractElement:
PackageDeclaration | Entity;
PackageDeclaration:
'package' name=QualifiedName '{'
elements+=AbstractElement*
'}';
Entity:
'entity' name=ValidID ('extends' superType=JvmTypeReference)? '{'
features+=Feature*
'}';
Feature:
Property | Operation;
Property:
name=ValidID ':' type=JvmTypeReference;
Operation:
'op' name=ValidID
'('(params+=FullJvmFormalParameter
(',' params+=FullJvmFormalParameter)*)?')'
':' type=JvmTypeReference
body=XBlockExpression;
</code></pre>
<p>Some parts of this grammar are equal to the one in the <a href="102_domainmodelwalkthrough.html">15 Minutes Tutorial</a>, but other parts are different.</p>
<ol>
<li>
<pre><code class="language-xtext">grammar org.example.domainmodel.Domainmodel with
org.eclipse.xtext.xbase.Xbase
</code></pre>
<p>The first thing to note is that instead of inheriting from the usual <em>org.eclipse.xtext.common.Terminals</em> grammar, we make use of <em>org.eclipse.xtext.xbase.Xbase</em>. Xbase allows us to easily reuse and embed modern, statically typed expressions as well as Java type signatures in our language.</p>
</li>
<li>
<pre><code class="language-xtext">Domainmodel:
importSection=XImportSection?
elements+=AbstractElement*;
</code></pre>
<p>A <em>Domainmodel</em> contains an optional import section and an arbitrary number of <em>AbstractElements</em>. The concept of <em>XImportSection</em> is part of <em>org.eclipse.xtext.xbase.Xbase</em> and comes with tool support and syntax like you know it from Java.</p>
</li>
<li>
<pre><code class="language-xtext">AbstractElement:
PackageDeclaration | Entity;
</code></pre>
<p>The rule <em>AbstractElement</em> delegates to either the rule <em>PackageDeclaration</em> or the rule <em>Entity</em>.</p>
</li>
<li>
<pre><code class="language-xtext">PackageDeclaration:
'package' name=QualifiedName '{'
elements+=AbstractElement*
'}';
</code></pre>
<p>A <em>PackageDeclaration</em> is used to declare a name space which can again contain any number of <em>AbstractElement</em>s. Xtext has built-in support for qualified names and scoping based on the hierarchy of the produced model. The default implementation will add the package names as the prefix to contained entities and nested packages. The qualified name of an <em>Entity</em> ‘Baz’ which is contained in a <em>PackageDeclaration</em> ‘foo.bar’ will be ‘foo.bar.Baz’. In case you do not like the default behavior you will need to use a different implementation of <a href="https://github.com/eclipse/xtext-core/blob/master/org.eclipse.xtext/src/org/eclipse/xtext/naming/IQualifiedNameProvider.java">IQualifiedNameProvider</a>.</p>
</li>
<li>
<pre><code class="language-xtext">Entity:
'entity' name=ValidID ('extends' superType=JvmTypeReference)? '{'
features+=Feature*
'}';
</code></pre>
<p>The rule <em>Entity</em> starts with the definition of a keyword followed by a name. The <em>extends</em> clause makes use of the rule <em>JvmTypeReference</em> which is defined in the super grammar. <em>JvmTypeReference</em> defines the syntax for full Java-like type names. That is everything from simple names, over fully qualified names to fully-fledged generics, including wildcards, lower bounds and upper bounds. Finally between curly braces there can be any number of <em>Features</em>, which leads us to the next rule.</p>
</li>
<li>
<pre><code class="language-xtext">Feature:
Property | Operation;
</code></pre>
<p>The rule <em>Feature</em> delegates to either a <em>Property</em> or an <em>Operation</em>.</p>
</li>
<li>
<pre><code class="language-xtext">Property:
name=ValidID ':' type=JvmTypeReference;
</code></pre>
<p>A <em>Property</em> has a name and makes again use of the inherited rule <em>JvmTypeReference</em>.</p>
</li>
<li>
<pre><code class="language-xtext">Operation:
'op' name=ValidID
'('(params+=FullJvmFormalParameter
(',' params+=FullJvmFormalParameter)*)?')'
':' type=JvmTypeReference
body=XBlockExpression;
</code></pre>
<p><em>Operation</em>s also have a signature as expected. Note that also for formal parameters we can reuse a rule from the super grammar.</p>
<p>The body of the <em>Operation</em> is the actual implementation and is defined by the rule <em>XBlockExpression</em>, which is one of the most often used entry rules from Xbase. A block consist of any number of expressions between curly braces such as:</p>
<pre><code class="language-xbase">{
return "Hello World" + "!"
}
</code></pre>
</li>
</ol>
<h2 id="domain-model-step3">Step Three: Generate Language Artifacts</h2>
<p>Now that we have the grammar in place we need to execute the code generator that will derive the various language components. To do so, right-click into the grammar editor and select</p>
<p><em>Run As → Generate Xtext Artifacts</em>.</p>
<p>This action generates the parser and text editor and some additional infrastructure code. You will see its logging messages in the Console View.</p>
<p><img src="images/JvmTutorial_RunGenerator.png" alt="" /></p>
<h2 id="domain-model-step4">Step Four: Define the Mapping to JVM Concepts</h2>
<p>The syntax alone is not enough to make the language work. We need to map the domain-specific concepts to some other language in order to instruct Xtext how it is executed. Usually you define a code generator or an interpreter for that matter, but languages using Xbase can omit this step and make use of the <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/IJvmModelInferrer.java">IJvmModelInferrer</a>.</p>
<p>The idea is that you translate your language concepts to any number of Java types (<a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.common.types/emf-gen/org/eclipse/xtext/common/types/JvmDeclaredType.java">JvmDeclaredType</a>). Such a type can be a Java class, Java interface, Java annotation type or a Java enum and may contain any valid members. In the end you as a language developer are responsible to create a correct model according to the Java language.</p>
<p>By mapping your language concepts to Java elements, you implicitly tell Xtext in what kind of scopes the various expressions live and what return types are expected from them. Xtext also comes with a code generator which can translate that Java model into readable Java code, including the expressions.</p>
<p>If you have already triggered the ‘Generate Xtext Artifacts’ action, you should find a stub called <em>org/example/domainmodel/jvmmodel/DomainmodelJvmModelInferrer.xtend</em> in the src folder. Please replace its contents with the following :</p>
<pre><code class="language-xtend">package org.example.domainmodel.jvmmodel
import com.google.inject.Inject
import org.example.domainmodel.domainmodel.Entity
import org.example.domainmodel.domainmodel.Operation
import org.example.domainmodel.domainmodel.Property
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
class DomainmodelJvmModelInferrer extends AbstractModelInferrer {
/**
* a builder API to programmatically create Jvm elements
* in readable way.
*/
@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
def dispatch void infer(Entity element,
IJvmDeclaredTypeAcceptor acceptor,
boolean isPrelinkingPhase) {
acceptor.accept(element.toClass( element.fullyQualifiedName )) [
documentation = element.documentation
if (element.superType !== null)
superTypes += element.superType.cloneWithProxies
for (feature : element.features) {
switch feature {
Property : {
members += feature.toField(feature.name, feature.type)
members += feature.toGetter(feature.name, feature.type)
members += feature.toSetter(feature.name, feature.type)
}
Operation : {
members += feature.toMethod(feature.name, feature.type) [
documentation = feature.documentation
for (p : feature.params) {
parameters += p.toParameter(p.name, p.parameterType)
}
body = feature.body
]
}
}
}
]
}
}
</code></pre>
<p>Let’s go through the code to get an idea of what is going on. (Please also refer to the JavaDoc of the used API for details, especially the <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/JvmTypesBuilder.java">JvmTypesBuilder</a>).</p>
<ol>
<li>
<pre><code class="language-xtend">def dispatch void infer(Entity element,
IJvmDeclaredTypeAcceptor acceptor,
boolean isPrelinkingPhase) {
</code></pre>
<p>Using the dispatch keyword makes sure that the method is called for instances of type <em>Entity</em> only. Have a look at the Xtend documentation on <a href="http://www.eclipse.org/xtend/documentation/202_xtend_classes_members.html#polymorphic-dispatch">polymorphic dispatch</a> to understand Xtend’s dispatch functions. Extending <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/AbstractModelInferrer.java">AbstractModelInferrer</a> makes sure we don’t have to walk the syntax model on our own.</p>
</li>
<li>
<pre><code class="language-xtend">acceptor.accept(element.toClass(element.fullyQualifiedName)) [
...
]
</code></pre>
<p>Every <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.common.types/emf-gen/org/eclipse/xtext/common/types/JvmDeclaredType.java">JvmDeclaredType</a> you create in the model inference needs to be passed to the <em>acceptor</em> in order to get recognized. The extension method <em>toClass</em> comes from the <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/JvmTypesBuilder.java">JvmTypesBuilder</a> class. This class provides a lot of convenient extension methods, which help making the code extremely readable and concise. It is important to understand that the creation and assignment of a qualified name is done in an early phase where the compiler collects all global symbols. You cannot resolve type references at this point.</p>
</li>
<li>The second argument to <em>accept</em> is a lambda block that contains further initialization of the created Java type. Only in this block you are allowed to do type reference resolution. This is also the place where you add members and put the XExpressions into context. Let’s see what we do in the initialization block in detail:</li>
<li>
<pre><code class="language-xtend">documentation = element.documentation
</code></pre>
<p>Here we assign some JavaDoc to the newly created element. The assignment is translated to an invocation of the method <em><a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/JvmTypesBuilder.java">JvmTypesBuilder</a>.setDocumentation(<a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.common.types/emf-gen/org/eclipse/xtext/common/types/JvmIdentifiableElement.java">JvmIdentifiableElement</a>, String)</em>, and <code>element.documentation</code> is in fact calling the extension method <em><a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/JvmTypesBuilder.java">JvmTypesBuilder</a>.getDocumentation(<a href="https://git.eclipse.org/r/plugins/gitiles/emf/org.eclipse.emf/+/refs/tags/R2_20_0/plugins/org.eclipse.emf.ecore/src/org/eclipse/emf/ecore/EObject.java">EObject</a>)</em>. Such extension methods are explained in detail in the <a href="https://www.eclipse.org/xtend/documentation/202_xtend_classes_members.html#extension-methods">Xtend documentation</a>.</p>
</li>
<li>
<pre><code class="language-xtend">if (element.superType !== null)
superTypes += entity.superType.cloneWithProxies
</code></pre>
<p>Set the <em>superType</em> on the inferred element. Note that we have to clone the type reference from the <em>element.superType</em>. If we did not do that, the type reference would be automatically removed from the <em>element</em>, as <em>superType</em> is an EMF containment reference.</p>
</li>
<li>
<pre><code class="language-xtend">for (feature : element.features) {
switch feature {
Property : {
// ...
}
Operation : {
// ...
}
}
}
</code></pre>
<p>When iterating over a list of heterogeneous types, the switch expression with its type guards comes in handy. If <em>feature</em> is of type <em>Property</em> the first block is executed. If it is an <em>Operation</em> the second block is executed. Note that the variable <em>feature</em> will be implicitly casted to the respective type within the blocks.</p>
</li>
<li>
<pre><code class="language-xtend">Property : {
members += feature.toField(feature.name, feature.type)
members += feature.toSetter(feature.name, feature.type)
members += feature.toGetter(feature.name, feature.type)
}
</code></pre>
<p>For each <em>Property</em> we create a field as well as a corresponding getter and setter.</p>
</li>
<li>
<pre><code class="language-xtend">Operation : {
members += feature.toMethod(feature.name, feature.type) [
documentation = feature.documentation
for (p : feature.params) {
parameters += p.toParameter(p.name, p.parameterType)
}
body = feature.body
]
}
</code></pre>
<p><em>Operations</em> are mapped to a corresponding Java method. The documentation is translated and the parameters are added within the initializer. The line <code>body = feature.body</code> registers the <em>Operation</em>’s expression as the body of the newly created Java method. This defines the scope of the expression. The framework deduces the visible fields and parameters as well as the expected return type from that information.</p>
</li>
</ol>
<h2 id="domain-model-step5">Step Five : Try the Editor!</h2>
<p>We are now able to test the IDE integration by spawning a new Eclipse using our plug-ins: right-click the project <code>org.example.domainmodel</code> in the Package Explorer and select <em>Run As → Eclipse Application</em>.</p>
<p>In the new workbench, create a Java project (<em>File → New → Project… → Java Project</em>). Xbase relies on a small runtime library on the class path. To add this, right-click on the project and go to <em>Java Build Path → Libraries → Add Library</em> and choose <em>Xtend Library</em>. Then create a new file with the file extension you chose in the beginning (<em>*.dmodel</em>) in the source folder of the Java project. This will open the generated entity editor. Try it and discover the rich editor functionality it provides. You should also have a look at the preferences of your language to find out what can be individually configured to your users needs.</p>
<p>Have fun!</p>
<p><img src="images/JvmTutorial_Editor.png" alt="" /></p>
<hr />
<p><strong><a href="301_grammarlanguage.html">Next Chapter: The Grammar Language</a></strong></p>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="site-footer">
<div id="extra">
<div class="inner">
<div class="container">
<div class="row">
<div class="span6">
<h3 class="footer-links-header">Quick Links</h3>
<ul class="footer-links clearfix">
<li><a href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a></li>
<li><a href="http://www.eclipse.org/legal/termsofuse.php">Terms of Use</a></li>
<li><a href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a></li>
<li><a href="http://www.eclipse.org/legal/">Legal</a></li>
</ul>
<ul class="footer-links clearfix">
<li><a href="http://www.eclipse.org">Eclipse Home</a></li>
<li><a href="http://marketplace.eclipse.org/">Market Place</a></li>
<li><a href="http://www.planeteclipse.org/">Eclipse Planet</a></li>
<li><a href="https://www.eclipse.org/forums/index.php/f/27/">Xtext Forum</a></li>
</ul>
</div>
<div class="span6">
<!-- Social Media Links -->
<h3 class="footer-links-header"">Social Media</h3>
<ul class="footer-links clearfix">
<li>
<a href="https://twitter.com/xtext"><img src="/Xtext/images/Twitter-bird-darkgray.png" class="img-responsive" style="margin-right: 5px;height: 1em;" alt="Twitter icon">@xtext on Twitter</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<a href="#" class="scrollup fadeOutRight animated" style="display: none;">ScrollUp</a>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="/Xtext/js/jquery-1.11.3.min.js"></script>
<script src="/Xtext/js/bootstrap.min.js"></script>
<script src="/Xtext/js/jquery.easing.1.3.js" type="text/javascript"></script>
<script src="/Xtext/js/jquery.prettyPhoto.js" type="text/javascript"></script>
<script src="/Xtext/js/prettify.js" type="text/javascript"></script>
<script src="/Xtext/js/lang-xtend.js" type="text/javascript"></script>
<script src="/Xtext/js/lang-common.js" type="text/javascript"></script>
<script src="/Xtext/js/custom.js" type="text/javascript"></script>
<!--script src="https://apis.google.com/js/platform.js" async defer></script-->
<!--script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push([ '_setAccount', 'UA-2429174-3' ]);
_gaq.push([ '_trackPageview' ]);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl'
: 'http://www')
+ '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script-->
<script src="/Xtext/js/coverflow.min.js" type="text/javascript"></script>
<script>
$(function() {
$('#coverflow').coverflow({
active : 1,
visibleAside: 2,
overlap : 0.5,
scale : 0.9,
angle : 20,
trigger : {
"itemfocus" : true,
"swipe" : true,
"mousewheel" : false
}
});
$('#coverflow :hidden').toggle();
$(window).resize(function() {
$('#coverflow').coverflow();
});
});
</script>
</footer>
</body>
</html>