1. NatTable DataSources

Usually data sources for the NatTable are Java objects, which are provided as a list.

Visualizing data from a generic data structure can be very helpful. For example showing data based on a JSON or XML data structure.

2. Exercise - Visualizing JSON data

2.1. Target

The target of this exercise is to visualize a generic JSON data structure in a NatTable.

2.2. Obtaining the dependencies

Reuse the existing example application from the general NatTable tutorial or create a new E4 Sample application.

Adjust the existing target definition or create a new one with the following contents.

target definition gson

2.3. The JSON data

In the root of the plugin project a books.json file should be created:

[
  {
    "id" : "978-0641723445",
    "cat" : ["book","hardcover"],
    "name" : "The Lightning Thief",
    "author" : "Rick Riordan",
    "genre" : "fantasy",
    "inStock" : true,
    "price" : 12.50,
    "pages" : 384
  }
,
  {
    "id" : "978-1423103349",
    "cat" : ["book","paperback"],
    "name" : "The Sea of Monsters",
    "author" : "Rick Riordan",
    "genre" : "fantasy",
    "inStock" : true,
    "price" : 6.49,
    "pages" : 304
  }
,
  {
    "id" : "978-1857995879",
    "cat" : ["book","paperback"],
    "name" : "Sophie's World : The Greek Philosophers",
    "author" : "Jostein Gaarder",
    "genre" : "fantasy",
    "inStock" : true,
    "price" : 3.07,
    "pages" : 64
  }
,
  {
    "id" : "978-1933988177",
    "cat" : ["book","paperback"],
    "name" : "Lucene in Action, Second Edition",
    "author" : "Michael McCandless",
    "genre" : "IT",
    "inStock" : true,
    "price" : 30.50,
    "pages" : 475
  }
]

2.4. Creating proper NatTable data provider

To provide data for a NatTable different IDataProvider are necessary. One for the header and one for the body of the NatTable.

The one for the header takes the keys of the first element of a com.google.gson.JsonArray and specifies these keys as column header labels.

package com.vogella.nattable.data;

import java.io.Reader;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.nebula.widgets.nattable.data.IDataProvider;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class JsonColumnHeaderDataProvider implements IDataProvider {


    private List<String> colums;

    public JsonColumnHeaderDataProvider(Reader reader) {
        this(new JsonParser().parse(reader));
    }

    public JsonColumnHeaderDataProvider(JsonElement jsonArray) {
        JsonArray asJsonArray = jsonArray.getAsJsonArray();
        JsonObject jsonObject = asJsonArray.get(0).getAsJsonObject();
        Set<Entry<String,JsonElement>> entrySet = jsonObject.entrySet();
        colums = entrySet.stream().map(entry -> entry.getKey()).collect(Collectors.toList());
    }

    @Override
    public int getColumnCount() {
        return this.colums.size();
    }

    @Override
    public int getRowCount() {
        return 1;
    }

    @Override
    public Object getDataValue(int columnIndex, int rowIndex) {
        return getColumnHeaderLabel(columnIndex);
    }

    private Object getColumnHeaderLabel(int columnIndex) {
        return colums.get(columnIndex);
    }

    @Override
    public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
        throw new UnsupportedOperationException();
    }

}

For the body a IColumnPropertyAccessor for JsonElement objects is used.

package com.vogella.nattable.data;

import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

public class JsonColumnAccessor implements IColumnPropertyAccessor<JsonElement> {

    private List<String> colums;

    public JsonColumnAccessor(JsonElement jsonElement) {
        JsonArray jsonArray = jsonElement.getAsJsonArray();
        JsonElement firstJsonEntry = jsonArray.get(0);
        JsonObject jsonObject = firstJsonEntry.getAsJsonObject();
        Set<Entry<String,JsonElement>> entrySet = jsonObject.entrySet();

        colums = entrySet.stream().map(entry -> entry.getKey()).collect(Collectors.toList());
    }

    @Override
    public int getColumnCount() {
        return colums.size();
    }

    @Override
    public String getColumnProperty(int columnIndex) {
        return colums.get(columnIndex);
    }

    @Override
    public int getColumnIndex(String propertyName) {
        return colums.indexOf(propertyName);
    }

    @Override
    public Object getDataValue(JsonElement rowObject, int columnIndex) {
        return rowObject.getAsJsonObject().get(getColumnProperty(columnIndex));
    }

    @Override
    public void setDataValue(JsonElement rowObject, int columnIndex, Object newValue) {
        // TODO
    }

}

A IRowDataProvider for JsonElement objects will make use of the JsonColumnAccessor and manage the underlying data for the NatTable.

package com.vogella.nattable.data;

import java.io.Reader;
import java.util.Iterator;

import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;
import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

public class JsonDataProvider implements IRowDataProvider<JsonElement> {

    private JsonArray jsonArray;
    private IColumnAccessor<JsonElement> columnAccessor;

    public JsonDataProvider(Reader reader, IColumnAccessor<JsonElement> columnAccessor) {
        this(new JsonParser().parse(reader).getAsJsonArray(), columnAccessor);
    }

    public JsonDataProvider(JsonArray jsonArray, IColumnAccessor<JsonElement> columnAccessor) {
        this.jsonArray = jsonArray;
        this.columnAccessor = columnAccessor;
    }

    @Override
    public int getColumnCount() {
        return this.columnAccessor.getColumnCount();
    }

    @Override
    public int getRowCount() {
        return this.jsonArray.size();
    }

    @Override
    public Object getDataValue(int columnIndex, int rowIndex) {
        JsonElement rowObj = this.jsonArray.get(rowIndex);
        return this.columnAccessor.getDataValue(rowObj, columnIndex);
    }

    @Override
    public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
        JsonElement rowObj = this.jsonArray.get(rowIndex);
        this.columnAccessor.setDataValue(rowObj, columnIndex, newValue);
    }

    @Override
    public JsonElement getRowObject(int rowIndex) {
        return this.jsonArray.get(rowIndex);
    }

    @Override
    public int indexOfRowObject(JsonElement rowObject) {
        int index = 0;
        for (Iterator<JsonElement> iterator = jsonArray.iterator(); iterator.hasNext();) {
            JsonElement element = iterator.next();
            index++;
            if(rowObject.equals(element)) {
                return index;
            }
        }

        return -1;
    }
}

2.5. Creating the JSON data part

Now create a new part for the E4 application, which will read the books.json file and visualized this in a NatTable.

package com.vogella.nattable.parts;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URL;

import javax.annotation.PostConstruct;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
import org.eclipse.nebula.widgets.nattable.layer.CompositeLayer;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
import org.eclipse.swt.widgets.Composite;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.vogella.nattable.data.JsonColumnAccessor;
import com.vogella.nattable.data.JsonColumnHeaderDataProvider;
import com.vogella.nattable.data.JsonDataProvider;

public class GsonPart {

    @PostConstruct
    public void postConstruct(Composite parent) throws IOException {

        Reader reader = getJsonTestReader();

        JsonParser jsonParser = new JsonParser();

        JsonElement jsonElement = jsonParser.parse(reader);

        JsonDataProvider jsonDataProvider = new JsonDataProvider(jsonElement.getAsJsonArray(),
                new JsonColumnAccessor(jsonElement));

        DataLayer dataLayer = new DataLayer(jsonDataProvider);
        SelectionLayer selectionLayer = new SelectionLayer(dataLayer);
        ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);

        IDataProvider headerDataProvider = new JsonColumnHeaderDataProvider(jsonElement);
        DataLayer headerDataLayer = new DataLayer(headerDataProvider);
        ILayer columnHeaderLayer = new ColumnHeaderLayer(headerDataLayer, viewportLayer, selectionLayer);

        CompositeLayer compositeLayer = new CompositeLayer(1, 2);
        compositeLayer.setChildLayer(GridRegion.COLUMN_HEADER, columnHeaderLayer, 0, 0);
        compositeLayer.setChildLayer(GridRegion.BODY, viewportLayer, 0, 1);

        new NatTable(parent, compositeLayer);
    }

    private Reader getJsonTestReader() throws IOException, UnsupportedEncodingException {
        Bundle bundle = FrameworkUtil.getBundle(getClass());
        URL find = FileLocator.find(bundle, new Path("books.json"), null);
        InputStream is = find.openStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        return br;
    }

}

2.6. Validate

When running the application the GsonPart part should look like this:

gson part result

3. NatTableDataSources resources

4. vogella training and consulting support

Copyright © 2012-2019 vogella GmbH. Free use of the software examples is granted under the terms of the Eclipse Public License 2.0. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.