blob: 64d0448236a56662b64015f01a5c461e0561bc3a [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Xtext - DSL for MongoDB</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]-->
</head>
<body>
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-TGDS5S"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-TGDS5S');</script>
<!-- End Google Tag Manager -->
<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://typefox.io/trainings-2" 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/204_mongodb.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">Seven JVM Languages Built With Xbase</li>
<li><a href="201_sevenlang_introduction.html">Introduction</a></li>
<li><a href="202_scripting.html">Scripting Language</a></li>
<li><a href="203_builddsl.html">Build Language</a></li>
<li><a href="204_mongodb.html">DSL for MongoDB</a></li>
<li><a href="205_guice.html">DSL for Guice</a></li>
<li><a href="206_httprouting.html">Http Routing Language</a></li>
<li><a href="207_template.html">Template Language</a></li>
<li><a href="208_tortoise.html">Little Tortoise</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="306_mwe2.html">MWE2</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="320_idea_support.html">IntelliJ IDEA Support</a></li-->
<li><a href="330_web_support.html">Web Editor Support</a></li>
<li><a href="350_continuous_integration.html">Continuous Integration</a></li>
<!--li class="nav-part">Appendix</li>
<li><a href="401_migrating_from_1_0_x.html">Migrating from Xtext 1.0.x to 2.0</a></li>
<li><a href="402_migrating_from_0_7.html">Migrating from Xtext 0.7.x to 1.0</a></li-->
</ul>
</div>
<div class="span8 doc-contents">
<h1 id="mongoDB">DSL for MongoDB</h1>
<p><a href="http://www.mongodb.org">MongoDB</a> is a very popular document-based database management system. In mongoDB, database entries (AKA <em>documents</em>) have fields, which are essentially <em>(key, value)</em> pairs. MongoDB is schema free, i.e. there are no rules, which fields have to be defined and of what type they are. This allows for very flexible and heterogeneous data structures and is a perfect match with JSON.</p>
<p><img src="images/mongobeans_screenshot.png" alt="" /></p>
<p>OTOH, Java is statically typed: The available types, their field names and field types are known and validated at compile time. The JavaBeans convention defines how instances can be manipulated. The standard mongoDB Java driver reflects the fact that mongoDB is schema-free by providing mongo documents as plain Java maps.</p>
<p>With this language you can describe statically typed Java-facades for MongoDB documents without hiding the dynamic nature of them. The language uses a tree-like syntax similar to JSON but lets you add static Java type information.</p>
<h2 id="mongo-solution">Overview</h2>
<p>In this project, we have created a small DSL <em>mongoBeans</em> based on Xtext that allows to create basic entity classes. These are backed by mongoDB objects but provide a statically typed JavaBeans API. Think of the language as a description how to map mongoDB documents to JavaBeans, in analogy to well known object relational mappers.</p>
<p>An example mongoBeans file looks like this:</p>
<pre><code class="language-mongobeans">import java.util.*
package org.musicdb {
// a mongo bean
Artist {
String name // single valued property
Album* albums // array property
// an operation
Iterable&lt;Track&gt; getOeuvre() {
albums.map[track].flatten
}
}
// another mongo bean
Album {
String title
int year
// inline definition of a mongo bean
Track {
String title
int seconds
}* tracks
}
}
</code></pre>
<p>For each <em>MongoBean</em> definition in a <em>MongoFile</em> file, we generate a Java class that wraps a <a href="http://api.mongodb.org/java/2.6/com/mongodb/DBObject.html">DBObject</a>. The class provides statically typed getter and setter methods for all defined <em>MongoProperties</em>. In the implementation of these accessor methods we delegate to the wrapped <a href="http://api.mongodb.org/java/2.6/com/mongodb/DBObject.html">DBObject</a> and do all the casting and conversion work. For the <code>Artist</code> in the above example, this would look like</p>
<pre><code class="language-java">public class Artist implements IMongoBean {
private DBObject _dbObject;
...
public String getName() {
return (String) _dbObject.get("name");
}
public void setName(final String name) {
_dbObject.put("name", name);
}
...
}
</code></pre>
<p>By using the generated Java code, the rest of the application can use a type-safe and JavaBeans conformant API to access the data model. In addition, <em>MongoBeans</em> can define <em>MongoOperations</em>, which are translated to Java methods. We can use <em>MongoProperties</em> as well as Java types inside the operations’ bodies.</p>
<p>Client code could then look like this:</p>
<pre><code class="language-java">Artist john = new Artist();
john.setName("John Coltrane");
Album album = new Album();
album.setTitle("A Love Supreme");
john.getAlbums().add(album);
Track... // create some tracks and add them to the album
System.out.println(john.getName() + "'s Oeuvre");
for(Track track: john.getOeuvre())
System.out.println(track.getTitle());
DBCollection dbCollection = ... // standard mongoDB driver code
dbCollection.save(john.getDBObject())
</code></pre>
<h2 id="mongo-running">Running the Example</h2>
<p>In addition to the <a href="201_sevenlang_introduction.html#common-requirements">common requirements</a>, you need the <a href="http://www.mongodb.org/downloads">mongoDB implementation</a> for your platform. We have included the mongoDB Java driver from <a href="http://download.eclipse.org/tools/orbit/downloads/">Eclipse Orbit</a> in the code base.</p>
<p>Import the projects into an Eclipse workspace and run the launch configuration <em>Run (org.eclipse.xtext.mongobeans)</em>. Import the example plug-in into the new workspace and run <code>MusicDBXtendTest</code> as a JUnit test.</p>
<h2 id="mongo-grammar">Grammar</h2>
<p>The complete mongoBeans grammar looks like this:</p>
<pre><code class="language-xtext">grammar org.xtext.mongobeans.MongoBeans with org.eclipse.xtext.xbase.Xbase
generate mongoBeans "http://www.eclipse.org/xtext/mongobeans/MongoBeans"
MongoFile:
importSection=XImportSection?
elements+=AbstractElement*;
AbstractElement:
PackageDeclaration | MongoBean;
PackageDeclaration:
'package' name=QualifiedName '{'
elements+=AbstractElement*
'}';
MongoBean:
name=ValidID '{'
features+=AbstractFeature*
'}';
AbstractFeature:
MongoOperation | MongoProperty;
MongoProperty:
(type=JvmTypeReference | inlineType=MongoBean) (many?='*')? name=ValidID;
MongoOperation:
=&gt;(returnType=JvmTypeReference name=ValidID '(')
(parameters+=FullJvmFormalParameter
(',' parameters+=FullJvmFormalParameter)*
)?
')'
body=XBlockExpression;
</code></pre>
<p>The language inherits from the <code>Xbase</code> grammar in order to allow Xbase expressions and references to Java elements. A <em>MongoFile</em> starts with an import section (see <a href="203_builddsl.html#builddsl-imports">Build DSL</a> for details). The import section is followed by any number of <em>AbstractElements</em>, which can be <em>PackageDeclarations</em> or <em>MongoBeans</em>. Note that as opposed to Java, <em>PackageDeclarations</em> can be nested. <em>MongoBeans</em> define statically typed <em>MongoProperties</em>, which can be single-valued or multi-valued denoted by an <code>*</code> following the type name. The type of a <em>MongoProperty</em> can also be defined inline. <em>MongoBeans</em> can also define <em>MongoOperations</em>. The body of such an operation is an <em>XBlockExpression</em> from Xbase.</p>
<h2 id="mongo-inferrer">Translation to Java</h2>
<p>The JVM model inference is implemented in the <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans/src/org/xtext/mongobeans/jvmmodel/MongoBeansJvmModelInferrer.xtend">MongoBeansJvmModelInferrer</a>. As the generated code is quite rich, this is the most complex component of this language.</p>
<p>For each <em>MongoBean</em>, we create a Java class implementing the interface <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.lib/src/org/xtext/mongobeans/lib/IMongoBean.xtend">IMongoBean</a>. This interface is the first type of a small runtime library that has to be on the classpath at runtime.</p>
<ul>
<li>
<p>DSL:</p>
<pre><code class="language-mongobeans">package org.musicdb {
Artist { ...
</code></pre>
</li>
<li>
<p>Java:</p>
<pre><code class="language-java">package org.musicdb;
...
public class Artist implements IMongoBean { ...
</code></pre>
</li>
</ul>
<p>The inferrer code responsible for this section looks like this:</p>
<pre><code class="language-xtend">@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
...
def dispatch void infer(MongoFile file,
IJvmDeclaredTypeAcceptor acceptor,
boolean isPreIndexingPhase) {
for(bean : file.eAllOfType(MongoBean)) {
acceptor.accept(bean.toClass(bean.fullyQualifiedName))[
documentation = bean.documentation
superTypes += typeRef(IMongoBean)
... // calling various methods to create Java members
// from the AbstractFeatures
]
}
}
</code></pre>
<p>First, it finds all elements of type <em>MongoBean</em> in the given <em>MongoFile</em>. For each of these, it creates a new Java class. Then the documentation is copied and the interface <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.lib/src/org/xtext/mongobeans/lib/IMongoBean.xtend">IMongoBean</a> is added to the list of supertypes. This will also insert a Java import at the appropriate location.</p>
<p>Each <em>MongoBean</em> wraps a <a href="http://api.mongodb.org/java/2.6/com/mongodb/DBObject.html">DBObject</a>, which is represented as a Java field with a getter. There are two constructors, one for a given <a href="http://api.mongodb.org/java/2.6/com/mongodb/DBObject.html">DBObject</a> and one that creates a new one. We have to store the class name in the DB object, if we want to be able to restore JavaBeans from query results.</p>
<ul>
<li>
<p>DSL:</p>
<pre><code class="language-mongobeans">Artist { ...
</code></pre>
</li>
<li>
<p>Java:</p>
<pre><code class="language-java">public class Artist implements IMongoBean {
private DBObject _dbObject;
public DBObject getDbObject() {
return this._dbObject;
}
public Artist(final DBObject dbObject) {
this._dbObject = dbObject;
}
public Artist() {
_dbObject = new BasicDBObject();
_dbObject.put(JAVA_CLASS_KEY, "org.musicdb.Artist");
}
...
</code></pre>
</li>
</ul>
<p>The inferrer code does this in two separate methods: One for the property <code>_dbObject</code> and another for the constructors.</p>
<pre><code class="language-xtend">def protected addDbObjectProperty(JvmDeclaredType
inferredType,
MongoBean bean) {
inferredType.members += bean.toField('_dbObject', typeRef(DBObject))
inferredType.members += bean.toGetter('dbObject', '_dbObject', typeRef(DBObject))
}
def protected addConstructors(JvmDeclaredType inferredType,
MongoBean bean) {
inferredType.members += bean.toConstructor [
documentation = '''...'''
parameters += bean.toParameter("dbObject", typeRef(DBObject))
body = '''
this._dbObject = dbObject;
'''
]
inferredType.members += bean.toConstructor [
documentation = '''...'''
body = '''
_dbObject = new com.mongodb.BasicDBObject();
_dbObject.put(JAVA_CLASS_KEY, "«inferredType.identifier»");
'''
]
}
</code></pre>
<p>Next on our list are the getters and setters delegating to the <code>_dbObject</code>. We have to handle four cases: Properties with a type that can be handled by the mongoDB Java-driver directly (most primitive types, <a href="http://docs.oracle.com/javase/8/docs/api//java/lang/String.html">String</a>, <a href="http://docs.oracle.com/javase/8/docs/api//java/util/Date.html">Date</a>, etc.), <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.lib/src/org/xtext/mongobeans/lib/IMongoBean.xtend">IMongoBean</a> properties, and their respective multi-valued counterparts:</p>
<ul>
<li>
<p>DSL:</p>
<pre><code class="language-mongobeans">...
String name // primitive property
Artist friend // bean-type property
String* aliases // multi-valued primitive property
Album* albums // multi-valued bean-type property
...
</code></pre>
</li>
<li>
<p>Java:</p>
<pre><code class="language-java">...
public String getName() {
return (String) _dbObject.get("name");
}
public void setName(final String name) {
_dbObject.put("name", name);
}
public Artist getFriend() {
return WrappingUtil.wrapAndCast(
(DBObject) _dbObject.get("friend"));
}
public void setFriend(final Artist friend) {
_dbObject.put("friend", WrappingUtil.unwrap(friend));
}
public List&lt;String&gt; getAliases() {
return (List&lt;String&gt;) _dbObject.get("aliases");
}
private MongoBeanList&lt;Album&gt; _albums;
public List&lt;Album&gt; getAlbums() {
if(_albums==null)
_albums = new MongoBeanList&lt;Album&gt;(_dbObject, "albums");
return _albums;
}
...
</code></pre>
</li>
</ul>
<p>The runtime helper class <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.lib/src/org/xtext/mongobeans/lib/WrappingUtil.xtend">WrappingUtil</a> does the conversion between <a href="http://api.mongodb.org/java/2.6/com/mongodb/DBObject.html">DBObject</a> and <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.lib/src/org/xtext/mongobeans/lib/IMongoBean.xtend">IMongoBean</a>. For multi-valued <em>MongoProperties</em> we need getters only. If they have a primitive type, they can be handled by the Java-driver directly. Multi-valued <em>MongoBean</em> typed properties require a special <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.lib/src/org/xtext/mongobeans/lib/MongoBeanList.xtend">MongoBeanList</a> to automatically wrap/unwrap the elements.</p>
<p>The corresponding inferrer code does not show anything particularly new so we skip most of it for brevity. To detect whether a type is an <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.lib/src/org/xtext/mongobeans/lib/IMongoBean.xtend">IMongoBean</a> or a primitive mongoDB type, we use the last helper class <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans/src/org/xtext/mongobeans/jvmmodel/MongoTypes.xtend">MongoTypes</a>. The following snippet shows the inference of the getter for multi-valued properties:</p>
<pre><code class="language-xtend">def protected addListAccessor(JvmDeclaredType inferredType,
MongoProperty property) {
val propertyType = property.jvmType.asWrapperTypeIfPrimitive
if(propertyType.isMongoPrimitiveType) {
inferredType.members += property.toMethod(
'get' + property.name.toFirstUpper,
typeRef(List, propertyType)
) [
...
]
} else {
inferredType.members += property.toField(
'_' + property.name, typeRef(MongoBeanList, propertyType))
inferredType.members += property.toMethod(
'get' + property.name.toFirstUpper, typeRef(List, propertyType)
) [
...
</code></pre>
<p>Last but not least, we infer Java methods for <em>MongoOperations</em>.</p>
<ul>
<li>
<p>DSL:</p>
<pre><code class="language-mongobeans">...
Iterable&lt;Track&gt; getOeuvre() {
albums.map[tracks].flatten
}
...
</code></pre>
</li>
<li>
<p>Java:</p>
<pre><code class="language-java">...
public Iterable&lt;Track&gt; getOeuvre() {
// some java code you really don't care about
// but it should just do the right thing
}
...
</code></pre>
</li>
</ul>
<p>The inferrer code for this particular task is surprisingly simple, as we can directly associate the body of the <em>MongoOperation</em> to the generated Java method. The Xbase compiler will automatically transform that to Java.</p>
<pre><code class="language-xtend">def protected addMethod(JvmDeclaredType inferredType,
MongoOperation operation) {
inferredType.members += operation.toMethod(operation.name,
operation.returnType) [
documentation = operation.documentation
for(parameter: operation.parameters)
parameters += parameter.toParameter(parameter.name,
parameter.parameterType)
body = operation.body
]
}
</code></pre>
<h2 id="mongo-name-provider">Qualified Name Provider</h2>
<p>By default, the qualified name of an element is calculated by joining all the simple names of its containers with a dot. In our example, the <em>MongoBean</em> <code>Track</code> would consequently be named <code>org.musicdb.Album.track.Track</code>. To ignore properties and beans on the path, we implemented our own <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans/src/org/xtext/mongobeans/scoping/MongoQualifiedNameProvider.xtend">MongoQualifiedNameProvider</a>.</p>
<pre><code class="language-xtend">class MongoQualifiedNameProvider extends XbaseQualifiedNameProvider {
def qualifiedName(MongoBean mongoBean) {
val packageDeclaration =
mongoBean.getContainerOfType(PackageDeclaration)
if(packageDeclaration != null)
packageDeclaration.fullyQualifiedName.append(mongoBean.name)
else
return QualifiedName.create(mongoBean.name)
}
}
</code></pre>
<p>To make the framework pick up our customization, we have to add a binding in the respective <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans/src/org/xtext/mongobeans/MongoBeansRuntimeModule.java">Guice module</a>.</p>
<pre><code class="language-java">@Override
public Class&lt;? extends IQualifiedNameProvider&gt; bindIQualifiedNameProvider() {
return MongoQualifiedNameProvider.class;
}
</code></pre>
<p>See <a href="302_configuration.html#dependency-injection">Dependency Injection</a> to learn more about Xtext’s dependency injection.</p>
<h2 id="mongo-validation">Validation</h2>
<p>The Java driver for mongoDB cannot map all Java types to mongoDB types. To enforce that constraint, we have added the <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans/src/org/xtext/mongobeans/validation/MongoBeansValidator.xtend">MongoBeansValidator</a>. It also checks for missing types and avoids name collisions in the generated code with the implicitly defined property <code>dbObject</code>.</p>
<pre><code class="language-xtend">class MongoBeansValidator extends XbaseJavaValidator {
...
@Inject extension MongoTypes mongoTypes
@Check def checkMongoProperty(MongoProperty it) {
if (name == 'dbObject')
error("Illegal property name 'dbObject'",
ABSTRACT_FEATURE__NAME,
ILLEGAL_PROPERTY_NAME,
'_' + name)
if (type != null) {
if (!type.isMongoType)
error('Only MongoBeans and mappable types are allowed',
MONGO_PROPERTY__TYPE, ILLEGAL_TYPE)
} else if (inlineType == null) {
error('Type must be set', ABSTRACT_FEATURE__NAME, MISSING_TYPE)
}
}
...
</code></pre>
<h2 id="mongo-visual">IDE Enhancements</h2>
<p>The validator from the previous section raises an error <code>ILLEGAL_PROPERTY_NAME</code> when a property is named <a href="http://api.mongodb.org/java/2.6/com/mongodb/DBObject.html">DBObject</a>. We have implemented a quick fix to replace the invalid name:</p>
<pre><code class="language-xtend">class MongoBeansQuickfixProvider extends XbaseWithAnnotationsQuickfixProvider {
@Fix(MongoBeansValidator.ILLEGAL_PROPERTY_NAME)
def void capitalizeName(Issue issue, IssueResolutionAcceptor acceptor) {
acceptor.accept(issue,
'Rename to ' + issue.data.head,
'''Rename property to «issue.data.head».''',
null) [
xtextDocument.replace(issue.offset, issue.length, issue.data.head)
]
}
}
</code></pre>
<p>To improve the language IDE visually, we have tuned the <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.ui/src/org/xtext/mongobeans/ui/outline/MongoBeansOutlineTreeProvider.xtend">outline</a> a bit</p>
<pre><code class="language-xtend">class MongoBeansOutlineTreeProvider extends DefaultOutlineTreeProvider {
// don't show children of operations
def _isLeaf(MongoOperation operation) {
true
}
// show inline declared MongoBeans
def _createChildren(IOutlineNode parentNode, MongoProperty property) {
if(property.inlineType != null)
parentNode.createNode(property.inlineType)
}
def _isLeaf(MongoProperty property) {
property.inlineType == null
}
}
</code></pre>
<p>and the <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.mongobeans.ui/src/org/xtext/mongobeans/ui/labeling/MongoBeansLabelProvider.xtend">label provider</a> as well:</p>
<pre><code class="language-xtend">class MongoBeansLabelProvider extends XbaseLabelProvider {
...
override image(Object element) {
// icons are stored in the 'icons' folder of this project
switch element {
MongoBean: 'Letter-B-blue-icon.png'
MongoProperty: 'Letter-P-orange-icon.png'
MongoOperation: 'Letter-O-red-icon.png'
Import: 'imp_obj.gif'
PackageDeclaration: 'package_obj.gif'
default:
super.image(element)
}
}
</code></pre>
<hr />
<p><strong><a href="205_guice.html">Next Chapter: DSL for Guice</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://live.eclipse.org/">Eclipse Live</a></li>
<li><a href="http://www.planeteclipse.org/">Eclipse Planet</a></li>
</ul>
</div>
<div class="span6">
<h3 class="footer-links-header"><a href="https://twitter.com/xtext" style="color: white;">@Xtext</a> on Twitter</h3>
<a class="twitter-timeline" href="https://twitter.com/xtext" data-widget-id="346625441290928128"
data-chrome="noheader nofooter transparent"
data-theme="dark">Tweets by @xtext</a>
<script>
!function(d,s,id) {
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
if(!d.getElementById(id)) {
js=d.createElement(s);
js.id=id;
js.src=p+"://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js,fjs);
}
}(document,"script","twitter-wjs");
</script>
</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/twitter.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>