Use Maven Artifact Version in Eclipse Code Templates


Based on waffel’s blog i wrote a eclipse plugin which provides the current artifact-version of a maven-project to the eclipse editor-templates. Waffel want to add the current plugin id/version to the @since field for class comments, i want to add the current version of my maven-eclipse-project. Let me explain my solution.

It’s easy to add a new template-variable to eclipse, you can read this. Based on  org.eclipse.jface.text.templates.TemplateVariableResolver we can write a MavenVersionResolver:


/**
 * Resolver to resolve variable <code>pomVersion</code>.
 * 
 * @author hoehmann
 * @since 1.0.0
 */
public class MavenVersionResolver extends TemplateVariableResolver {

  public MavenVersionResolver() {
    super();
  }

  private String getMavenVersion(final IProject project) {
    if (project == null) {
      throw new IllegalArgumentException("Missing project"); //$NON-NLS-1$
    }
    String result = ""; //$NON-NLS-1$
    try {
      if (project.hasNature(IMavenConstants.NATURE_ID)) {
        final MavenProjectManager projectManager = MavenPlugin.getDefault()
            .getMavenProjectManager();
        final IMavenProjectFacade projectFacade = projectManager.create(
            project, new NullProgressMonitor());
        if (projectFacade != null) {
          final ArtifactKey mavenProject = projectFacade.getArtifactKey();
          if (mavenProject != null) {
            result = mavenProject.getVersion();
            // remove snapshot-indicator
            final int index = result.lastIndexOf("-SNAPSHOT"); //$NON-NLS-1$
            if (index != -1) {
              result = result.substring(0, index);
            }
          }
        }
      }
    } catch (final CoreException ex) {
      MavenLogger.log(ex);
    }
    return result;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected String resolve(final TemplateContext context) {
    // TODO better way to get the project?!
    return getMavenVersion(((CodeTemplateContext) context).getJavaProject()
        .getProject());
  }
}

With the MavenProjectManager from m2eclipse we can create a IMavenProjectFacade, this facade returns the ArtifactKey and this key have the version. If the version is a snapshot-version we can cut this trailing string off and the result is the (next) version for our maven-project (for me it doesn’t make sense to add the snapshot-version into a @since comment because the release-version should be documented in the sourcecode).

Maybe the check for the „m2eclipse“-nature is not necessary:

if (project.hasNature(IMavenConstants.NATURE_ID)) {....}

I tried without the nature-check and it works. The project must contain a „pom.xml“ to get a IMavenProjectFacade.

This was the first part of the solution. The placeholder „pom_version“ will be available for all editor-templates in the „java-context“:

maven_version_editortemplate

Waffel described already a solution (a workaround) to use a editor-template-resolver in the code-templates. He registered a IStartup class which copies his own BundleIdResolver/BundleVersionResolver into the (internal) code-template-context-registry of the Eclipse-Java-Plugin. For waffel this was fine because he doesn’t register his resolvers as editor-template-resolvers. I want use my MavenVersionResolver in all java-templates and in the code-templates.

And i don’t want create a new instance of the resolver, i want reuse the extension-point-configured resolver. So i have only one place to define my resolver (type = ‚pom_version‘, localized name, localized description, class etc.).

I found a other way to register the resolver

  1. i search my MavenVersionResolver in the registered editor-templates (java-context)
  2. if i found one, i add this reference to the (internal) code-template-registry


/**
 * Currently it's not possible to provide more variables for
 * <tt>java-code-templates</tt>, we can only add more <tt>editor-templates</tt>
 * via extension-point.
 * 
 * <p>
 * This {@link IStartup} is a workaround to register our
 * {@link MavenVersionResolver} for <tt>java-code-templates</tt> too.
 * </p>
 * 
 * @author hoehmann
 * @since 1.0.0
 */
public class RegisterResolvers implements IStartup {

  private static final String JAVA_PLUGIN_ID = "org.eclipse.jdt.ui"; //$NON-NLS-1$

  /**
   * Add our resolver to each registered code-template-context.
   * 
   * @param javaPlugin
   *          must not be <code>null</code>
   * @param mavenVersionResolver
   *          must not be <code>null</code>
   */
  private void addMavenVersionResolver(final JavaPlugin javaPlugin,
      final MavenVersionResolver mavenVersionResolver) {
    Assert.isNotNull(javaPlugin);
    final ContextTypeRegistry codeTemplateContextRegistry = javaPlugin
        .getCodeTemplateContextRegistry();
    Assert.isNotNull(codeTemplateContextRegistry);
    final Iterator ctIter = codeTemplateContextRegistry.contextTypes();
    while (ctIter.hasNext()) {
      final TemplateContextType contextType = (TemplateContextType) ctIter
          .next();
      contextType.addResolver(mavenVersionResolver);
    }
  }

  /**
   * {@inheritDoc}
   */
  public void earlyStartup() {
    // check if plug-in org.eclipse.jdt.ui is final already active
    final Bundle bundle = Platform.getBundle(JAVA_PLUGIN_ID);
    if (bundle != null && bundle.getState() == Bundle.ACTIVE) {
      registerResolvers();
    } else {
      // register listener to final get informed, when plug-in final becomes
      // active
      final BundleContext bundleContext = Activator.getDefault().getBundle()
          .getBundleContext();
      bundleContext.addBundleListener(new BundleListener() {
        public void bundleChanged(final BundleEvent pEvent) {
          final Bundle eventBundle = pEvent.getBundle();
          if (!eventBundle.getSymbolicName().equals(JAVA_PLUGIN_ID)) {
            // ignore other plugins
            return;
          }
          if (eventBundle.getState() == Bundle.ACTIVE) {
            registerResolvers();
            bundleContext.removeBundleListener(this);
          }
        }
      });
    }
  }

  /**
   * Try to find our {@link MavenVersionResolver} in the java-plugin
   * template-context-registry.
   * 
   * @param javaPlugin
   *          must not be <code>null</code>
   * @return
   */
  private MavenVersionResolver getMavenVersionResolver(
      final JavaPlugin javaPlugin) {
    Assert.isNotNull(javaPlugin);
    final ContextTypeRegistry contextRegistry = javaPlugin
        .getTemplateContextRegistry();
    Assert.isNotNull(contextRegistry);
    final TemplateContextType javaContextType = contextRegistry
        .getContextType(JavaContextType.ID_ALL);
    Assert.isNotNull(javaContextType);
    final Iterator<TemplateVariableResolver> resolvers = javaContextType
        .resolvers();
    MavenVersionResolver mavenVersionResolver = null;
    while (resolvers.hasNext()) {
      final TemplateVariableResolver resolver = resolvers.next();
      if (resolver instanceof MavenVersionResolver) {
        mavenVersionResolver = (MavenVersionResolver) resolver;
        break;
      }
    }
    return mavenVersionResolver;
  }

  /**
   * First find the maven-version-resolver from the registered resolvers.
   */
  private void registerResolvers() {
    final JavaPlugin javaPlugin = JavaPlugin.getDefault();
    if (javaPlugin == null) {
      throw new IllegalStateException(String.format(
          "Expected plugin '%s' is not available", JAVA_PLUGIN_ID));
    }
    final MavenVersionResolver mavenVersionResolver = getMavenVersionResolver(javaPlugin);
    if (mavenVersionResolver != null) {
      addMavenVersionResolver(javaPlugin, mavenVersionResolver);
    }
  }
}

Now its possible to use „pom_version“ in code-templates too:

maven_version_codetemplate

Now the final test  …  create a „normal“ java-project, create a new class. The javadoc will not contain a version (the project doesn’t have a maven-nature):

maven_version_test_without_m2eclipse

If the project is a „real“ maven project the version will be available:

maven_version_test_with_m2eclipse

If anyone need the plugin … leave a comment.