grep command to filter distinct values from XML tags

I have a ton of Oracle Forms XML export files and wanted to know, which different patterns occur for the value of the FormatMask XML attribute. The input looks as follows:

<Item Name="CREATION_DATE" UpdateAllowed="false" DirtyInfo="false" Visible="false" QueryAllowed="false" InsertAllowed="false" Comment="TABLE ALIAS&amp;#10;  FDA&amp;#10;&amp;#10;BASED ON TABLE&amp;#10;  TMI_FINANCIAL_DATA&amp;#10;&amp;#10;COLUMN USAGES&amp;#10;  ...    CREATION_DATE                 SEL&amp;#10;" ParentModule="OBJLIB1" Width="10" Required="false" ColumnName="CREATION_DATE" DataType="Date" ParentModuleType="25" Label="Creation Date" ParentType="15" ParentName="QMSSO$QUERY_ONLY_ITEM" MaximumLength="10" PersistentClientInfoLength="142" ParentFilename="tmiolb65_mla.olb" FormatMask="DD-MM-RRRR">

A naive grep command would print out the whole line, including the file name. After some iterations I came to the following command, which does what I want in a single line.

grep -R -h -o -e FormatMask=\"[^\"]* * | sed 's/FormatMask="//g' | sort | uniq

What the command does is:

  • grep recursively (-R) for a regular expression (-e)
  • search for FormatMask="<any-char-until-quotation>
  • print only the matching part of the line (-o). This will include the prefix FormatMask="
  • print without the file name (-h)
  • strip off the prefix with sed
  • sort the results alphabetically
  • remove duplicate lines (uniq)

The result (excerpt)is:

00
09
099
0999
0999999
09999999
0D0
0D999
0D9999
9
90
90D0
90D000
...

Enabling Spring in Scout applications

Today I am attending the first Scout User Day 2014 in Ludwigsburg, which is aligned with EclipseCon Europe 2014 starting tomorrow. Yesterday we had a pre-event dinner with some attendees and the organizers at the Rossknecht restaurant. IMG_3161IMG_3160 I got into a chat with Nejc Gasper, who will give a talk titled “Build a Scout backend with Spring” today. I was a bit surprised as he told me he did not manage to get Spring’s classpath scanning working yet. Since we are doing this in our application, I think it is worth writing now down what we had to do to get this working. The goal in our application is primarely to use Spring as dependency injection container, since the customer uses Spring in all their other Java based applications, too, and wanted us to do so also.

Spring Configuration

The Spring configuration files are located in the folder META-INF/spring of the *.client, *.shared, *.server projects. In this configuration files, we mainly activate classpath scanning:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.rhenus.fl" />
    <!--  http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/validation.html#core-convert-Spring-config -->
    <beans:bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService" />
</beans:beans>

The next important thing is to copy the files spring.handlers, spring.schemas<, spring.tooling into the META-INF folder. The files can be found in the META-INF directory of bundle org.springframework.context. screenshot 21 Without doing this, you will get errors while loading the Spring configuration like this:

Caused by: 
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/context]|Offending resource: URL [bundleresource://9.fwk1993775065:1/META-INF/spring/fl_client.xml]|
	at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70)
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:80)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionParserDelegate.java:316)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1421)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1414)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:187)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:141)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:110)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:391)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:335)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:303)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180)
	at org.springframework.context.support.GenericXmlApplicationContext.load(GenericXmlApplicationContext.java:116)

Bundle Activator

The Spring configuration files are loaded in the Bundle Activator classes of the three “main” Scout projects (client/shared/server). The Activator can also be used then to access the ApplicationContext. We use the GenericXmlApplicationContext to initialize the context from the XML configuration above. One important thing is that this class uses the ClassLoader of the Activator. Otherwise you will get again the error mentioned in the section above. The Activator class looks then as follows:


public class Activator extends Plugin {

 // The plug-in ID
 public static final String PLUGIN_ID = "com.rhenus.fl.application.client";
 public final static String SPRING_CONFIG_FILE = "META-INF/spring/fl_client.xml";

 // The shared instance
 private static Activator plugin;
 private ApplicationContext ctx;

 @Override
 public void start(BundleContext context) throws Exception {
  super.start(context);
  plugin = this;
  init(context);
 }

 @Override
 public void stop(BundleContext context) throws Exception {
  plugin = null;
  super.stop(context);
 }

 public static Activator getDefault() {
  return plugin;
 }

 private void init(BundleReference bundleContext) {
  URL url = getClass().getClassLoader().getResource(SPRING_CONFIG_FILE);

  UrlResource usr = new UrlResource(url);

  ctx = new GenericXmlApplicationContext() {
  @Override
  public ClassLoader getClassLoader() {
   return Activator.class.getClassLoader();
  }
 };
  ((GenericXmlApplicationContext) ctx).load(usr);
  ((AbstractApplicationContext) ctx).refresh();

 }

 public ApplicationContext getContext() {
  return ctx;
 }
}

Service Factory

In order to use dependency injection in Scout services, the services themselves must be instantiated through the Spring ApplicationContext. The default implementation of course is not aware of Spring, so we need to customize this. Unfortunately we have to copy the class org.eclipse.scout.rt.server.services.ServerServiceFactory. We need just to exchange one single line in the method updateInstanceCache(), where the service is instantiated, but this method is private in Scout. The line

m_service = m_serviceClass.newInstance();

is replaced by

m_service = getContext().getBean(m_serviceClass);

Since we have to provide different ApplicationContexts in the different plugins, we put this into the abstract class AbstractSpringAwareServerServiceFactory (full code):

public abstract class AbstractSpringAwareServerServiceFactory implements IServiceFactory {

 private void updateInstanceCache(ServiceRegistration registration) {
  synchronized (m_serviceLock) {
   if (m_service == null) {
    try {
     // CUSTOMIZING BEGIN
//     m_service = m_serviceClass.newInstance();
     m_service = getContext().getBean(m_serviceClass);
     // CUSTOMIZING END
     if (m_service instanceof IService2) {
      ((IService2) m_service).initializeService(registration);
     } else if (m_service instanceof IService) {
      ((IService) m_service).initializeService(registration);
     }
    } catch (Throwable t) {
     LOG.error("Failed creating instance of " + m_serviceClass,
       t);
    }
   }
  }
 }
 
 // CUSTOMIZING BEGIN
 protected abstract ApplicationContext getContext();
 // CUSTOMIZING END

}

The concrete classes implement the method getContext() by accessing the method from the Bundle Activator:

public class ServerServiceFactory extends AbstractSpringAwareServerServiceFactory {

  /**
   * @param serviceClass
   */
  public ServerServiceFactory(Class<?> serviceClass) {
    super(serviceClass);
  }

  @Override
  protected ApplicationContext getContext() {
    return Activator.getDefault().getContext();
  }

}

plugin.xml

The service factory class implemented above must be used now to create the services. This is done in the plugin.xml file:

<service
 factory="com.rhenus.fl.application.server.services.ServerServiceFactory"
 class="com.rhenus.fl.tmi.server.tmirln010.TMIRLN010Service"
 session="com.rhenus.fl.application.server.ServerSession">
</service>

Use Dependency Injection

Now we are finally able to use Dependency Injection with javax.inject.Inject with Scout services.

import org.springframework.stereotype.Component;
import javax.inject.Inject;
...

@Component
@InputValidation(IValidationStrategy.PROCESS.class)
public class TMIRLN010Service extends AbstractTMIRLN010Service {
  @Inject
  protected ConversionService conversionService;
  ...
}

Go!

If everything is correct, you will now recognize the following lines in the console when starting up the Scout application:

Okt 27, 2014 8:45:28 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from URL [bundleresource://9.fwk1993775065:1/META-INF/spring/fl_client.xml]
Okt 27, 2014 8:45:29 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from URL [bundleresource://10.fwk1993775065:1/META-INF/spring/fl_shared.xml]
Okt 27, 2014 8:45:29 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing com.rhenus.fl.application.shared.Activator$1@b40d694: startup date [Mon Oct 27 08:45:29 CET 2014]; root of context hierarchy
Okt 27, 2014 8:45:29 AM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Okt 27, 2014 8:45:29 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing com.rhenus.fl.application.client.Activator$1@292f062b: startup date [Mon Oct 27 08:45:29 CET 2014]; root of context hierarchy
Okt 27, 2014 8:45:29 AM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor 
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring

Scout tables with fixed columns

For tables with many columns it is often better if some of the first columns stay fixed when the user scrolls the table content horizontally. Since RAP 2.0, tables and trees support fixed columns, but Scout (current: 4.0/Eclipse Luna) does not provide this feature for its Table implementation. Since Scout supports different UI frameworks (Swing,SWT,RAP) and only RAP provides a table implementation with fixed columns support, this might be a reason for this missing feature.

The necessary additions can be added to Scout without changing Scout sources themselves. Scout allows to customize UI components, including existing ones. We can add this feature with fragments. The code mentioned below can be fetched from Github “scout-experimental”.

Client Extension Plugin

AbstractTableWithFixedColumns

First, we need a Table implementation that provides an additional configuration property fixedColumns. The value is stored using the element’s Property Support:

package org.eclipse.scout.rt.extension.client.ui.basic.table;

import org.eclipse.scout.commons.annotations.ConfigProperty;

/**
 * A table that supports Fixed Columns.
 *
 * @see http://eclipse.org/rap/developers-guide/devguide.php?topic=tree-table.html&version=2.0
 */
public class AbstractTableWithFixedColumns extends AbstractExtensibleTable {
	public static final String PROP_FIXED_COLUMNS = "org.eclipse.rap.rwt.fixedColumns"; // =RWT.FIXED_COLUMNS
	/**
	 * Configures how many of the visible columns should be fixed.
	 * @return A value greater than 0
	 */
	@ConfigProperty(ConfigProperty.INTEGER)
	protected int getConfiguredFixedColumns () {
		return -1;
	}

	public void setFixedColumns (int n) {
		propertySupport.setPropertyInt(PROP_FIXED_COLUMNS, n);
	}

	public int getFixedColumns () {
		return propertySupport.getPropertyInt(PROP_FIXED_COLUMNS);
	}

	@Override
	protected void initConfig() {
		super.initConfig();
		setFixedColumns(getConfiguredFixedColumns());
	}

}

This custom class can be specified as default base class for tables in the Scout workspace preferences:
Scout base class

In the client code, the table field would be defined like this:

public class CtyTableField
      extends
      AbstractTableField<CtyTableField.Table> {
          public class Table extends AbstractTableWithFixedColumns {
             @Override
             protected int getConfiguredFixedColumns() {
                return 2;
             }
          }
}

Scout RAP UI extension fragment

RwtScoutTableExt

The class that must be customized is org.eclipse.scout.rt.ui.rap.basic.table.RwtScoutTable. I’m not good in creating names, so I call its extension just RwtScoutTableExt.


package org.eclipse.scout.rt.ui.rap.basic.table;

import org.eclipse.rap.rwt.RWT;
import org.eclipse.scout.rt.client.ui.basic.table.columns.IColumn;
import org.eclipse.scout.rt.ui.rap.ext.table.TableEx;
import org.eclipse.swt.widgets.Composite;

public class RwtScoutTableExt extends RwtScoutTable {
	@Override
	protected void initializeUi(Composite parent) {
	    super.initializeUi(parent);
	    TableEx table = getUiField();
		// Fixed Columns Support
		// see
		// http://eclipse.org/rap/developers-guide/devguide.php?topic=tree-table.html&version=2.0
		// the configured fixed columns refer to visible columns only, but for
		// the RWT table the number of fixed
		// columns include also the non-visible ones. Compute how many columns
		// should be fixed in the RWT table.
		Integer configuredFixedColumns = (Integer) getScoutObject().getProperty(
				RWT.FIXED_COLUMNS);
		if (configuredFixedColumns != null && configuredFixedColumns > 0) {
			int fixedColumns = 0;
			int visibleColumns = 0;
			for (IColumn<?> column : getScoutObject().getColumns()) {
				fixedColumns++;
				if (column.isDisplayable() && column.isInitialVisible()) {
					visibleColumns++;
				}
				if (visibleColumns == configuredFixedColumns) {
					break;
				}
			}
			table.setData(RWT.FIXED_COLUMNS, new Integer(fixedColumns));
		}
	}
}

After Scout has initialized the table component (call of super), the table has to be configured with the number of fixed columns:


table.setData(RWT.FIXED_COLUMNS, new Integer(fixedColumns));

The number of configured fixed columns has to be specified by a property of the table field. For now, you might wonder why we do not simply set the configured fixed column value. The reason is, that some columns might not be visible, and when configuring the fixed columns, you would expect that only the visible ones should count.

RwtScoutTableFieldExt

The table class itself is constructed by RwtScoutTableField. In order to construct our customized class, RwtScoutTableField must be subclassed and override the createRwtScoutTable() method:

package org.eclipse.scout.rt.ui.rap.form.fields.tablefield;

import org.eclipse.scout.rt.client.ui.form.fields.smartfield.IContentAssistFieldProposalForm;
import org.eclipse.scout.rt.ui.rap.basic.table.IRwtScoutTable;
import org.eclipse.scout.rt.ui.rap.basic.table.RwtScoutTable;
import org.eclipse.scout.rt.ui.rap.basic.table.RwtScoutTableExt;
import org.eclipse.scout.rt.ui.rap.util.RwtUtility;

public class RwtScoutTableFieldExt extends RwtScoutTableField {
	@Override
	protected IRwtScoutTable createRwtScoutTable() {
		if (getScoutObject().getForm() instanceof IContentAssistFieldProposalForm) {
			return new RwtScoutTable(RwtUtility.VARIANT_PROPOSAL_FORM);
		} else {
			return new RwtScoutTableExt();
		}
	}

}

MANIFEST.MF

The created fragment is a fragment of the bundle org.eclipse.scout.rt.ui.rap. At least from Scout version 3.9.0 on, likely earlier, the mentioned solution should work.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Scout UI RAP - Extension
Bundle-SymbolicName: de.kthoms.scout.rt.ui.rap;singleton:=true
Bundle-Version: 1.0.0.qualifier
Fragment-Host: org.eclipse.scout.rt.ui.rap;bundle-version="3.9.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Export-Package: org.eclipse.scout.rt.ui.rap.form.fields.tablefield

fragment.xml

The extended field class has to be configured in the fragment’s descriptor fragment.xml by using the org.eclipse.scout.rt.ui.rap.formfields extension point:


<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<fragment>
  <extension
        point="org.eclipse.scout.rt.ui.rap.formfields">
      <formField
            active="true"
            modelClass="org.eclipse.scout.rt.client.ui.form.fields.tablefield.ITableField"
            name="Table field"
            scope="global">
            <uiClass
              class="com.itemis.scout.rt.ui.rap.form.fields.tablefield.RwtScoutTableFieldExt">
            </uiClass>
      </formField>
  </extension>

</fragment>

Note the scope="global" configuration. The interface org.eclipse.scout.rt.ui.rap.extension.IFormFieldExtension defines 3 scopes: default, global, local. The description for the global scope is:

IFormFieldExtension.SCOPE_GLOBAL to indicate this extension to have a global scope (whole eclipse). Global defined extensions overwrite the default implementation.

Overwriting the default implementation is exactly what we try to achieve here.

Result

The first 2 columns are marked as fixed. In fact, in front of the first visible column the table has a non-displayable Id column. When scrolling the table horizontally, the columns “Iso 2 Code” and “Description” are fixed.

screenshot 2014-07-07 um 12.10.50
screenshot 2014-07-07 um 12.11.02

Remove “Build path specifies execution environment…” warnings from Problems View

I have often workspaces with projects which specify Java 1.5 as minimal execution environment. On my machine there is no JDK 1.5 installed, and it turns out that getting one for Mac OSX Mountain Lion is not trivial. Actually I don’t need a JDK 1.5, since the standard 1.6 JDK is compatible. However, this raises in the workspace these annoying warnings.

screenshot 2013-05-17 um 10.32.31

In the Execution Environments setting it is possible to mark the Java 1.6 installation as compatible to the J2SE-1.5 Execution Environment:

screenshot 2013-05-17 um 10.52.31

Although the JDK 1.6 is marked compatible now, it is not “strictly compatible”, so the warning message remains.

Next you could try to disable the warning message. There is a setting for this in the preference dialog Java/Compiler/Building:

screenshot 2013-05-17 um 10.35.17

After changing the setting you are asked to rebuild the projects. But again, the warning do not disappear. I suspect this to be a bug and raised Bug#408317 for this.

So the last chance is to filter these warnings. Therefore select the options menu in the Problems View and open the “Configure Contents” dialog. In the “Types” selection tree expand the “Java Build Path Problems” node and uncheck “JRE System Library Problem”.

screenshot 2013-05-17 um 11.03.53

Finally the warning messages disappear from the Problems View. However, they are just filtered from the view, the projects themselves will still have these resource markers, so you will have a warning overlay icon on the project in the Package Explorer View although the Problems View might be empty.

But this raises the next problem: Now the warning disappears and the code is compiled with Java 1.6, and thus against the 1.6 API. This leads to the problem that you could accidently use API from >= 1.6. For example, usage of String#isEmpty() would compile even if the Execution Environment is set to J2SE-1.5 (the Execution Environment anyway just defines the lowest requirement) and also if Java source compatibility is set to 1.6 in the compiler settings.

We need to detect this unwanted use of API that is not 1.5 compatible. Therefore the PDE tooling offers support to install an Execution Environment Description for J2SE-1.5 and set up API tooling. This will finally allow us to detect illegal API use:

screenshot 2013-05-17 um 14.00.15

I like to thank Laurent Goubet and Mikael Barbero for their valuable comments on the potential API problems.

Preparations for CodeGeneration 2013

As I am addicted to code generation and DSLs, the CodeGeneration conference in Cambridge is always a must each year. Last year I could not make it, since I had the chance to speak at EclipseCon North America, which was in the same week. This year Mark took EclipseCon into his considerations (it was last week), so me and my colleagues from itemis will be there again. Actually, this year we will be more itemis guys then ever. Mark already assumed in his opening words at CG2011 that almost everyone from itemis would be there, this year we prove itemis is larger. I think our company is so close related to the conference theme that it is natural that we have lots to present, and much interest to hear others about what they are doing and have learned in the past.

screenshot 2013-04-05 um 10.55.19

Before the actual CodeGeneration conference starts on Wednesday, there are some pre-conference activities. My colleagues Holger Schill and Moritz Eysholdt will hold an intensive 2-day Xtext workshop on monday and tuesday.

I will arrive monday noon, since I take the early flight directly from my hometown Dortmund to London Luton. From there, I have to take a 2 hour bus trip to Cambridge. In the evening, I plan to meet Holger, Moritz, Meinte Boersma and hopefully some others in the Castle Inn pub. When you arrive on monday, drop into the Castle Inn roughly at 20 PM (I guess we go to a restaurant before). You can reach me there on my mobile phone: screenshot 2013-04-05 um 12.30.02

DSC00412

On tuesday this year’s Language Workbench Challenge summit takes place. We have 14 submissions (wow!) for the LWC13 assignment. I have been working on the Xtext submission together with 2 colleagues, Johannes Dicks and Thomas Kutz. The results are available as open-source project lwc13-xtext at Eclipselabs. We have prepared a detailed step-by-step tutorial as submission paper. The resulting document LWC13-XtextSubmission.pdf is available for download. On the project homepage I have placed today a quick start tutorial. Oh boy, this project did cost some time. The actual solution is not much code, but as often, it is harder to write less code than more. It could be even less, but we took care of that the code is readable and understandable. And writing the document is at least that much work as the implementation.

screenshot 2013-04-08 um 13.52.06

Every presenter has only 15 minutes to present their approach. 15 minutes presentation for that much work. I guess that the other participants did invest also quite some time. Both of my co-authors got the chance to visit the conference, and Thomas will support me with the presentation. He will demo the resulting JSF application and DSL source code while I do the main talking. We did a test run of the talk yesterday evening, and easily exceeded 20 minutes. I think Angelo will bring his egg timer again, which begs no pardon with the talk time of speakers. But only that way we will be able to run 14 talks on one day. We will have to restrict on the most important aspects only.

Besides my colleagues Thomas and Johannes also Sven Lange will join us then. I have the pleasure to work with him in my long-time project at Deutsche Börse (German Stock Exchange), now since over half a year. Sven is a highly motivated, skilled and smart person. It is still the same project I have reported about at CodeGeneration 2009 together with my former colleague Heiko Behrens. Sven is full-time working on this project, while I am for 20% scheduled. We have migrated here a huge code generator project from Xpand to Xtend. This alone would be worth an experience report session. Sven is working on Xtend support for IntelliJ, which he might present in a Lightning Talk on wednesday.

Wednesday the conference will start. I will have my main talk “Alive and Kicking with Code Generation” together with Dr. Boris Baginski from ATOSS Software AG after lunch at 13:45.

screenshot 2013-04-05 um 10.57.34

Currently we are finalizing our presentation slides. Boris has been ill for some days and busy with a new release of their ASES product, a workforce management suite. This is really an interesting customer and project. They are evolving this product now for 25 years, and they make use of code generation for ages. I think one can say that it helped them to survive in their business, some contestants did not manage to make larger platform shifts and died. Most of them tried a big-bang replacement, but the business is too fast evolving so that the target is moving steadily. Boris and I will speak about this product and how it has been evolved over the years. ATOSS was one of the first major projects using openArchitectureWare 4 (which mainly means Xpand), and now they are currently preparing a shift to Xtend.

I am glad that this talk is already on wednesday, I never come to rest until I finished some talk. After it, I can just relax and enjoy the conference. I am expecting some interesting insights on different approaches. Especially experience reports are interesting for me. I did not finally decide which sessions I will attend. At the moment I plan to see John Hutchinson with “The Use of Model-Driven Development in Industry” in the morning, and Darius Silingas with “Why MDA Fails: Analysis of Unsuccessful Cases” in the afternoon.

In the evening it is again time for the punting boat tour. I already attended three times, but it will be great fun again for sure. Let’s hope the weather is not too bad. I saw a prediction of ~10°C and possibility of light shower. In the past we had luck, and on a warm, sunny day the tour is double fun. However, I’ll better put an umbrella into the suitcase.

On thursday I have again an active part in the hands-on session “Have Your Language Built While You Wait”, which is hosted by Risto Pohjonen from MetaCase. The idea of this session is that attendees can get a DSL with the language workbench of their choice built with the help of experts for this workbench. Of course I will assist on Xtext. If you had no chance to visit the Xtext workshop this might be your chance to get some hands on Xtext. This session was already run last year successfully. Last year my colleague Benjamin Schwertfeger took over the Xtext part, since we were at EclipseCon.

There are also some other talks around Xtext and Xtend. Both have been released in version 2.4 on March 20th, which brings some interesting new features. Most notably in regard to code generation are the Active Annotations. I guess this is also part of what Sven Efftinge will adress as future of code generation in his keynote “The Past, Present and Future of Code Generation” wednesday morning. More details he will present together with Sebastian Zarnekow in the tutorial “Internal DSLs with Xtend” (thursday 10:45-12:00). The last Xtext related talk will be from Moritz Eysholdt, called “Executable Specifications for Xtext Languages” (friday 10:45-12:15). I am actually not sure which of these talks I will attend personally. They are most relevant for my work, and I don’t work close enough with them to catch everything new in Xtext on my own. Thus, I’ll definetely would learn important aspects. On the other side, there are also other interesting talks in parallel.

The coming week will be an intensive experience with lots to learn and interesting persons to meet. Although I will really enjoy this time, I will be glad when I finally come back home. At the moment, my family is ill and I hope that I get not infected these days. I have been looking forward and worked for this event, so I am crossing fingers when I can board monday morning healthy.

I am sure the organizing team around Mark and Jacqui will do again a great job.

DSC00363

See you there and let’s make this event special!

Xtext Content Assist: Filtering keyword proposals

Sometimes when I introduce template proposals in Xtext editors I want them to be a replacement for a keyword proposal. By default you would get a proposal for the template AND for the keyword. In the following screenshot a template for a “class” concept exists, and a proposal for the “class” keyword, which would be inserted as part of the template at the cursor position.

The key to the solution is to overwrite the completeKeyword() method from your proposal provider class, e.g. like this:

public class SprayProposalProvider extends AbstractSprayProposalProvider {
    private static final Set&lt;String&gt;      FILTERED_KEYWORDS = Sets.newHashSet("text", "line", "class", "behavior", "style", "custom");
    @Override
    public void completeKeyword(Keyword keyword, ContentAssistContext contentAssistContext, ICompletionProposalAcceptor acceptor) {
        if (FILTERED_KEYWORDS.contains(keyword.getValue())) {
            // don't propose keyword
            return;
        }
        super.completeKeyword(keyword, contentAssistContext, acceptor);
    }
}

Christian Dietrich added in a comment how to filter a keyword that may occur more than once in your grammar. If you want to filter specific occurances you need to use the IGrammarAccess of your language:

@Inject MyDslGrammarAccess grammarAccess;

@Override
public void completeKeyword(Keyword keyword,ContentAssistContext contentAssistContext,ICompletionProposalAcceptor acceptor) {
  if (!grammarAccess.getGreetingAccess().getHelloKeyword_0().equals(keyword)) {
    super.completeKeyword(keyword, contentAssistContext, acceptor);
  }
}

Fornax Workflow Plugin 3.4.0 released

Finally I have managed to release a new version of the Fornax Maven Workflow Plugin again. This Maven plugin allows execution of MWE and MWE2 workflows from Maven, which is typically the case if you want to automate the build of Xtext or Xpand based projects with Maven.

The new version has some interesting new features which may be worth upgrading:

  • m2e lifecycle metadata
  • workflow dependencies can be configured as plugin dependencies
  • more options to detect changes that trigger the build
  • force/skip parameter to configure workflow execution overrides per configuration
  • log level detection when running in forked JVM

I have especially to thank Dieter Schulten from escalon, who has been willing to contribute to this release. He has mainly contributed the m2e configuration and some tests for the plugin.

xtext-utils unittesting 0.9.4 released

I have just released the new version 0.9.4 of the xtext-utils unittesting framework. The release became necessary due to a too restrictive version constraint for Google Guice, which made the framework incompatible with the upcoming Xtext 2.3.0 release.

This little framework is a useful addition to Xtext which makes writing DSL tests quite easy. In my daily work on developing Xtext projects and coaching Xtext in workshops it became a useful tool which complements Xtext’s test support. It is not the only way to write tests for Xtext DSLs, but a real simple one. The main idea is that the best way to develop DSLs is to write prototype models. This framework allows you to read, parse, validate, format, serialize, and compare your models in a single line.

The framework can be installed from its update site.

Unofficial release 2.2.2 of the Xtend Maven Plugin

In my previous post I showed how to use the maven-xtend-plugin to compile Xtend sources within a Maven build. This worked fine in simple cases, but there was a limitation of only one supported source directory (the one configured by build/sourceDirectory property), see Bug#367914. Unfortunately already each Xtext project has already 2 source folders (src and src-gen), so this made the plugin unusable for Xtext projects and forced checking in the generated Java files. This causes terrible problems with version control, especially when working on a team.

In the meantime Bug#367914 was resolved, but only for the upcoming Xtend 2.3 (Eclipse Juno) version. This urgent bugfix was not available on a Maven repository. But I needed it for project Spray to finally get rid of the merge conflicts and delete the generated sources from the repository (issue#94). Spray is still using Xtext/Xtend version 2.2.1 and depends on public available Maven artifacts. Since the bugfix won’t be available from the Xtend project for the 2.2.1 release and Eclipse Juno is still some way to go I decided to backport the current 2.3.0 M6 sources of the Xtend Maven Plugin to 2.2.x and create an unofficial version 2.2.2. This plugin I have deployed to the Fornax Repository to make it public available.

For an usage example refer to the POMs of project Spray: pom.xml, parent pom.xml.

JvmType import check

Several Xtext based DSLs will use import statements that import JvmTypes, be them real Java types, or just inferred types produced with your IJvmModelInferrer. The check for a valid import might be simple, but here it is to just make it even easier for you. I just had the need to realize this to resolve Spray issue#113. Basically you need to get the IJvmTypeProvider, to which you gain access through the IJvmTypeProvider.Factory.

public class SprayJavaValidator extends AbstractSprayJavaValidator implements IssueCodes {
    @Inject
    private IJvmTypeProvider.Factory typeProviderFactory;
    @Check
    public void checkImports(final Import imp) {
        // don't check wildcard imports
        if (imp.getImportedNamespace().endsWith(".*"))
            return;
        IJvmTypeProvider typeProvider = typeProviderFactory.findOrCreateTypeProvider(imp.eResource().getResourceSet());
        JvmType jvmType = typeProvider.findTypeByName(imp.getImportedNamespace());
        if (jvmType == null) {
            error("The import " + imp.getImportedNamespace() + " cannot be resolved", SprayPackage.Literals.IMPORT__IMPORTED_NAMESPACE, IMPORT_NOTEXISTS, new String[0]);
        }
    }
    ...
}

That’s all.