Sunday, October 08, 2017

Java 9: My favourite feature!

First of all, sorry about the "click-baity" title!

My favourite feature in Java 9 has to be the Javadoc Search.

The Java API documentation now has a search box which you can use to find packages, types and methods quickly. It would be nice if it supported regular expressions :)

Here is a screencast GIF showing the Javadoc Search in action, created using LICECap:

Saturday, October 07, 2017

Using XSLT 2.0 with Java and Saxon

In my previous post, I showed how you can split a string using the tokenize function in XSLT 2.0. In order to run an XSLT 2.0 stylesheet in Java, you need a transformer that supports XSLT 2.0. Unfortunately, the default JAXP transformer (com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl) does not. Instead, use Saxon, which supports both XSLT 2.0 and 3.0.

The class below shows how you can perform an XSL transformation in Java, using Saxon and the JAXP interface:

import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class XSLTransformer {

  private final Templates templates;

  public XSLTransformer(final String xslFileName) throws Exception {
    templates = new net.sf.saxon.BasicTransformerFactory().newTemplates(
        new StreamSource(XSLTransformer.class.getClassLoader()
                             .getResourceAsStream(xslFileName)));
  }

  public String transform(final String xml) throws Exception {
    final Transformer transformer = templates.newTransformer();
    final StringWriter writer = new StringWriter();
    transformer.transform(new StreamSource(new StringReader(xml)),
                          new StreamResult(writer));
    return writer.toString();
  }
}

An alternative to the JAXP interface is to use Saxon's own s9api interface, which is more robust:

import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.transform.stream.StreamSource;

import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.s9api.XsltTransformer;

public class SaxonTransformer {

  private final Processor processor;
  private final XsltExecutable xsltExec;

  public SaxonTransformer(final String xslFileName) throws SaxonApiException {
    processor = new Processor(false);
    xsltExec = processor.newXsltCompiler().compile(new StreamSource(
        XSLTransformer.class.getClassLoader().getResourceAsStream(xslFileName)));
  }

  public String transform(final String xml) throws Exception {
    final XsltTransformer transformer = xsltExec.load();
    transformer.setSource(new StreamSource(new StringReader(xml)));
    final StringWriter writer = new StringWriter();
    transformer.setDestination(processor.newSerializer(writer));
    transformer.transform();
    return writer.toString();
  }
}

Splitting a string using XSLT 2.0

The tokenize function available in XSL Transformations (XSLT) Version 2.0 allows you to split a string on any separator that matches a given regular expression.

The example below shows how you can split a comma-delimited string:

Input XML:

<data>
  <stringToSplit>foo,bar,baz</stringToSplit>
</data>

XSL 2.0 Stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="data">
    <items>
      <xsl:variable name="stringToSplit" select="stringToSplit" />
      <xsl:for-each select="tokenize($stringToSplit, ',')">
        <item>
          <xsl:value-of select="." />
        </item>
      </xsl:for-each>
    </items>
  </xsl:template>
</xsl:stylesheet>

Output XML:

<?xml version="1.0" encoding="UTF-8"?>
<items>
  <item>foo</item>
  <item>bar</item>
  <item>baz</item>
</items>

Sunday, October 01, 2017

Java 9: Creating Immutable Collections

Static factory methods have been added in JDK 9, to allow you to easily create immutable lists, sets and maps.

Immutable Lists

Use the List.of() static factory methods to create immutable lists.

// in JDK 9
List<String> list = List.of("foo", "bar");

// in JDK 8
List<String> list2 = Collections.unmodifiableList(Arrays.asList("foo", "bar"));

// in Guava
List<String> list3 = ImmutableList.of("foo", "bar");

Immutable Sets

Use the Set.of() static factory methods to create immutable sets.

// in JDK 9
Set<String> set = Set.of("foo", "bar");

// in JDK 8
Set<String> set2 = Collections.unmodifiableSet(
                       new HashSet<>(Arrays.asList("foo", "bar")));

// in Guava
Set<String> set3 = ImmutableSet.of("foo", "bar");

Immutable Maps

Use the Map.of() and Map.ofEntries() static factory methods to create immutable maps.

// in JDK 9
Map<String, Integer> map = Map.of("key1", 1, "key2", 2);
// for more than 10 key-value pairs, use Map.ofEntries
Map<String, Integer> map2 = Map.ofEntries(entry("key1", 1),
                                          entry("key2", 2));

// in JDK 8
Map<String, Integer> map3 = new HashMap<>();
map3.put("key1", 1);
map3.put("key2", 2);
map3 = Collections.unmodifiableMap(map3);

// in Guava
Map<String, Integer> map4 = ImmutableMap.of("key1", 1, "key2", 2);
// or, for more than 5 key-value pairs:
Map<String, Integer> map5 = ImmutableMap.<String, Integer>builder()
                                .put("key1", 1)
                                .put("key2", 2)
                                .build();

Implementation Details

It's interesting to look at the implementation of these static factory methods. They return a different class based on which method you use e.g. if you call List.of(e1) you get an instance of ImmutableCollections.List1 but if you call List.of(e1, e2) you get an instance of ImmutableCollections.List2. These are compact field-based classes that consume less heap space than their unmodifiable (or mutable) equivalents.

Note that the immutable collections returned by the static factory methods are not "wrapper" collections like those returned by Collections.unmodifiable. With an unmodifiable collection, you can still modify the underlying collection if you have a reference to it, but an immutable collection will always throw an exception if you try to modify it.

Java 9: Eclipse Installation

Java 9 is out! This post shows you how to get developing with Java 9 in Eclipse straightaway!

  1. Install JDK 9
  2. Install Eclipse Oxygen
  3. Install Java 9 Support for Oxygen in Eclipse
  4. Finally, go to "Window > Preferences > Java > Installed JREs" in Eclipse and add your Java 9 JRE

That's it!

Update (14-Oct-2017): Eclipse Oxygen.1a (4.7.1a) supports Java 9, so the "Java 9 Support for Oxygen" plugin is no longer required.