Home Tutorials Training Consulting Books Company Contact us


Get more...

This tutorial contains notes about NatTableDataSources.

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 jakarta.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