/**
* OrbisGIS is a java GIS application dedicated to research in GIScience.
* OrbisGIS is developed by the GIS group of the DECIDE team of the
* Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>.
*
* The GIS group of the DECIDE team is located at :
*
* Laboratoire Lab-STICC – CNRS UMR 6285
* Equipe DECIDE
* UNIVERSITÉ DE BRETAGNE-SUD
* Institut Universitaire de Technologie de Vannes
* 8, Rue Montaigne - BP 561 56017 Vannes Cedex
*
* OrbisGIS is distributed under GPL 3 license.
*
* Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488)
* Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285)
*
* This file is part of OrbisGIS.
*
* OrbisGIS is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OrbisGIS is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* OrbisGIS. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, please consult: <http://www.orbisgis.org/>
* or contact directly:
* info_at_ orbisgis.org
*/
package org.orbisgis.toolboxeditor.dataui;
import net.miginfocom.swing.MigLayout;
import net.opengis.wps._2_0.DescriptionType;
import net.opengis.wps._2_0.InputDescriptionType;
import net.opengis.wps._2_0.OutputDescriptionType;
import org.orbisgis.sif.common.ContainerItem;
import org.orbisgis.sif.components.renderers.JPanelListRenderer;
import org.orbisgis.toolboxeditor.ToolboxWpsClient;
import org.orbisgis.toolboxeditor.WpsClientImpl;
import org.orbisgis.toolboxeditor.utils.ToolBoxIcon;
import org.orbiswps.server.model.DataType;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;
import javax.swing.*;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.EventHandler;
import java.math.BigInteger;
import java.net.URI;
import java.util.*;
import java.util.List;
import org.orbiswps.server.model.JDBCColumn;
import org.orbiswps.server.model.JDBCValue;
/**
* DataUI implementation for JDBCColumn.
* This class generate an interactive UI dedicated to the configuration of a JDBCColumn.
* The interface generated will be used in the ProcessEditor.
*
* @author Sylvain PALOMINOS
* @author Erwan Bocher
**/
public class JDBCColumnUI implements DataUI {
/** Size constants **/
private static final int MAX_JLIST_ROW_COUNT_VERTICAL = 5;
private static final int MAX_JLIST_ROW_COUNT_HORIZONTAL = 3;
private static final int MIN_JLIST_ROW_COUNT = 1;
/** Constant used to pass object as client property throw JComponents **/
private static final String DATA_MAP_PROPERTY = "DATA_MAP_PROPERTY";
private static final String URI_PROPERTY = "URI_PROPERTY";
private static final String DATA_COLUMN_PROPERTY = "DATA_COLUMN_PROPERTY";
private static final String IS_OPTIONAL_PROPERTY = "IS_OPTIONAL_PROPERTY";
private static final String DEFAULT_ITEM_PROPERTY = "DEFAULT_ITEM_PROPERTY";
private static final String COLUMN_TITLE_PROPERTY = "COLUMN_TITLE_PROPERTY";
private static final String INITIAL_DELAY_PROPERTY = "INITIAL_DELAY_PROPERTY";
private static final String TOOLTIP_TEXT_PROPERTY = "TOOLTIP_TEXT_PROPERTY";
private static final String NULL_ITEM = "NULL_ITEM";
private static final String MAX_JLIST_ROW_COUNT = "MAX_JLIST_ROW_COUNT";
/** I18N object */
private static final I18n I18N = I18nFactory.getI18n(JDBCColumnUI.class);
/** WpsClient using the generated UI. */
private WpsClientImpl wpsClient;
public void setWpsClient(WpsClientImpl wpsClient){
this.wpsClient = wpsClient;
}
@Override
public JComponent createUI(DescriptionType inputOrOutput, Map<URI, Object> dataMap, Orientation orientation) {
JPanel panel = new JPanel(new MigLayout("fill, ins 0, gap 0"));
JDBCColumn jdbcColumn = null;
boolean isOptional = false;
//Retrieve the JDBCTable
//If it is an input, find if it is optional.
if(inputOrOutput instanceof InputDescriptionType){
jdbcColumn = (JDBCColumn)((InputDescriptionType)inputOrOutput).getDataDescription().getValue();
if(((InputDescriptionType)inputOrOutput).getMinOccurs().equals(new BigInteger("0"))){
isOptional = true;
}
}
else if(inputOrOutput instanceof OutputDescriptionType){
return null;
}
if(jdbcColumn == null){
return panel;
}
if(jdbcColumn.isMultiSelection()){
JList<ContainerItem<Object>> list = new JList<>();
DefaultListModel<ContainerItem<Object>> model = new DefaultListModel<>();
list.setModel(model);
list.setCellRenderer(new JPanelListRenderer());
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
list.setLayoutOrientation(JList.VERTICAL);
int maxRow;
if(orientation.equals(Orientation.VERTICAL)){
maxRow = MAX_JLIST_ROW_COUNT_VERTICAL;
}
else{
maxRow = MAX_JLIST_ROW_COUNT_HORIZONTAL;
}
list.putClientProperty(MAX_JLIST_ROW_COUNT, maxRow);
list.setVisibleRowCount(MIN_JLIST_ROW_COUNT);
URI uri = URI.create(inputOrOutput.getIdentifier().getValue());
list.putClientProperty(URI_PROPERTY, uri);
list.putClientProperty(DATA_COLUMN_PROPERTY, jdbcColumn);
list.putClientProperty(DATA_MAP_PROPERTY, dataMap);
list.putClientProperty(IS_OPTIONAL_PROPERTY, isOptional);
list.putClientProperty(COLUMN_TITLE_PROPERTY, URI.create(inputOrOutput.getTitle().get(0).getValue().replaceAll("[^a-zA-Z0-9_]", "_")));
list.addListSelectionListener(EventHandler.create(ListSelectionListener.class, this, "onItemSelected", "source"));
list.addMouseListener(EventHandler.create(MouseListener.class, this, "onComboBoxEntered", "source", "mouseEntered"));
list.addMouseListener(EventHandler.create(MouseListener.class, this, "onComboBoxExited", "source", "mouseExited"));
list.setToolTipText(inputOrOutput.getAbstract().get(0).getValue());
if(!isOptional && dataMap.containsKey(uri)) {
Object obj = dataMap.get(uri);
if(obj instanceof String[]){
String[] columns = (String[]) obj;
int[] indexes = new int[columns.length];
int i=0;
for(String column : columns){
model.add(0, new ContainerItem<Object>(column, column));
indexes[i] = i;
i++;
}
list.setSelectedIndices(indexes);
}
else {
model.add(0, new ContainerItem<>(dataMap.get(uri), dataMap.get(uri).toString()));
list.setSelectedIndex(0);
}
if(model.getSize()>maxRow){
list.setVisibleRowCount(maxRow);
}
else{
list.setVisibleRowCount(model.getSize());
}
}
JScrollPane listScroller = new JScrollPane(list);
panel.add(listScroller, "growx, wrap");
jdbcColumn.setSourceModified(true);
}
else {
//ComboBox the column list
JComboBox<ContainerItem<Object>> comboBox = new JComboBox<>();
comboBox.setRenderer(new JPanelListRenderer());
comboBox.setBackground(Color.WHITE);
ContainerItem<Object> defaultItem = new ContainerItem<Object>(I18N.tr("Select a column"), I18N.tr("Select a column"));
URI uri = URI.create(inputOrOutput.getIdentifier().getValue());
comboBox.putClientProperty(URI_PROPERTY, uri);
comboBox.putClientProperty(DATA_COLUMN_PROPERTY, jdbcColumn);
comboBox.putClientProperty(DATA_MAP_PROPERTY, dataMap);
comboBox.putClientProperty(IS_OPTIONAL_PROPERTY, isOptional);
comboBox.putClientProperty(DEFAULT_ITEM_PROPERTY, defaultItem);
comboBox.putClientProperty(COLUMN_TITLE_PROPERTY, URI.create(inputOrOutput.getTitle().get(0).getValue().replaceAll("[^a-zA-Z0-9_]", "_")));
comboBox.addItemListener(EventHandler.create(ItemListener.class, this, "onItemSelected", "source"));
comboBox.addMouseListener(EventHandler.create(MouseListener.class, this, "onComboBoxEntered", "source", "mouseEntered"));
comboBox.addPopupMenuListener(EventHandler.create(PopupMenuListener.class, this, "onComboBoxEntered", "source"));
comboBox.addMouseListener(EventHandler.create(MouseListener.class, this, "onComboBoxExited", "source", "mouseExited"));
comboBox.setToolTipText(inputOrOutput.getAbstract().get(0).getValue());
if(!isOptional && dataMap.containsKey(uri)) {
Object obj = dataMap.get(uri);
if(obj instanceof String[]){
String str = ((String[])obj)[0];
ContainerItem<Object> item = new ContainerItem<Object>(str, str);
comboBox.addItem(item);
comboBox.setSelectedItem(item);
}
else {
ContainerItem<Object> item = new ContainerItem<>(dataMap.get(uri), dataMap.get(uri).toString());
comboBox.addItem(item);
comboBox.setSelectedItem(item);
}
}
else{
comboBox.addItem(defaultItem);
}
panel.add(comboBox, "growx, wrap");
}
return panel;
}
@Override
public Map<URI, Object> getDefaultValue(DescriptionType inputOrOutput) {
Map<URI, Object> map = new HashMap<>();
JDBCColumn jdbcColumn = null;
if(inputOrOutput instanceof InputDescriptionType){
jdbcColumn = (JDBCColumn)((InputDescriptionType)inputOrOutput).getDataDescription().getValue();
}
else if(inputOrOutput instanceof OutputDescriptionType){
jdbcColumn = (JDBCColumn)((OutputDescriptionType)inputOrOutput).getDataDescription().getValue();
}
if(jdbcColumn.getDefaultValues() != null) {
map.put(URI.create(inputOrOutput.getIdentifier().getValue()), jdbcColumn.getDefaultValues());
}
return map;
}
@Override
public ImageIcon getIconFromData(DescriptionType inputOrOutput) {
return ToolBoxIcon.getIcon(ToolBoxIcon.JDBC_COLUMN);
}
/**
* When the mouse leaves the comboBox, reset the tooltipText delay.
* @param source The source JComboBox.
*/
public void onComboBoxExited(Object source){
if(source instanceof JComboBox) {
//Retrieve the client properties
JComboBox<ContainerItem<Object>> comboBox = (JComboBox) source;
if (comboBox.getItemCount() == 0) {
comboBox.setToolTipText((String) comboBox.getClientProperty(TOOLTIP_TEXT_PROPERTY));
ToolTipManager.sharedInstance().setInitialDelay((int) comboBox.getClientProperty(INITIAL_DELAY_PROPERTY));
}
}
else if(source instanceof JList){
//Retrieve the client properties
JList<ContainerItem<Object>> list = (JList) source;
DefaultListModel<ContainerItem<Object>> model = (DefaultListModel<ContainerItem<Object>>)list.getModel();
if (model.getSize() == 0) {
list.setToolTipText((String) list.getClientProperty(TOOLTIP_TEXT_PROPERTY));
ToolTipManager.sharedInstance().setInitialDelay((int) list.getClientProperty(INITIAL_DELAY_PROPERTY));
}
list.revalidate();
list.repaint();
}
}
/**
* When the mouse entered the JComboBox, update it according to his JDBCTable parent.
* @param source The source JComboBox.
*/
public void onComboBoxEntered(Object source){
if(source instanceof JComboBox) {
JComboBox<ContainerItem<Object>> comboBox = (JComboBox) source;
JDBCColumn jdbcColumn = (JDBCColumn) comboBox.getClientProperty(DATA_COLUMN_PROPERTY);
HashMap<URI, Object> dataMap = (HashMap) comboBox.getClientProperty(DATA_MAP_PROPERTY);
URI uri = (URI) comboBox.getClientProperty(URI_PROPERTY);
boolean isOptional = (boolean) comboBox.getClientProperty(IS_OPTIONAL_PROPERTY);
ContainerItem<Object> defaultItem = (ContainerItem<Object>)comboBox.getClientProperty(DEFAULT_ITEM_PROPERTY);
//If the JDBCTable related to the JDBCColumn has been modified, reload the jdbcColumn values
if (jdbcColumn.isSourceModified() || (comboBox.getSelectedItem() != null && comboBox.getSelectedItem().equals(defaultItem))) {
Object obj = dataMap.get(uri);
comboBox.removeItem(defaultItem);
jdbcColumn.setSourceModified(false);
comboBox.removeAllItems();
List<ContainerItem<Object>> listContainer = populateWithColumns(jdbcColumn, dataMap);
for(ContainerItem<Object> container : listContainer){
comboBox.addItem(container);
}
if(isOptional){
comboBox.addItem(new ContainerItem<Object>(NULL_ITEM, NULL_ITEM));
}
//Try to select the good column
boolean isSelection = false;
if(obj != null && !obj.equals(defaultItem.getLabel())){
String str;
if(obj instanceof String[]){
str = ((String[])obj)[0];
}
else{
str = obj.toString();
}
for (int i = 0; i < comboBox.getItemCount(); i++) {
if (str.toUpperCase().equals(comboBox.getItemAt(i).getLabel().toUpperCase()) ) {
comboBox.setSelectedIndex(i);
isSelection = true;
break;
}
}
}
if(!isSelection) {
String title = comboBox.getClientProperty(COLUMN_TITLE_PROPERTY).toString().toUpperCase();
for (int i = 0; i < comboBox.getItemCount(); i++) {
if (title.contains(comboBox.getItemAt(i).getLabel()) ||
comboBox.getItemAt(i).getLabel().contains(title)) {
comboBox.setSelectedIndex(i);
break;
}
}
}
}
//If the comboBox doesn't contains any values, it mean that the JDBCTable hasn't been well selected.
//So show a tooltip text to warn the user.
if (comboBox.getItemCount() == 0) {
comboBox.putClientProperty(INITIAL_DELAY_PROPERTY, ToolTipManager.sharedInstance().getInitialDelay());
comboBox.putClientProperty(TOOLTIP_TEXT_PROPERTY, comboBox.getToolTipText());
ToolTipManager.sharedInstance().setInitialDelay(0);
ToolTipManager.sharedInstance().setDismissDelay(2500);
String jdbcTableColumnStr = jdbcColumn.getJDBCTableIdentifier().toString();
comboBox.setToolTipText(I18N.tr("First configure the JDBCTable {0}.",
jdbcTableColumnStr.substring(jdbcTableColumnStr.lastIndexOf(":") + 1)));
ToolTipManager.sharedInstance().mouseMoved(
new MouseEvent(comboBox, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
}
comboBox.revalidate();
}
else if(source instanceof JList){
JList<ContainerItem<Object>> list = (JList) source;
JDBCColumn jdbcColumn = (JDBCColumn) list.getClientProperty(DATA_COLUMN_PROPERTY);
int maxRow = (int) list.getClientProperty(MAX_JLIST_ROW_COUNT);
URI uri = (URI) list.getClientProperty(URI_PROPERTY);
Map<URI, Object> dataMap = (Map) list.getClientProperty(DATA_MAP_PROPERTY);
DefaultListModel<ContainerItem<Object>> model = (DefaultListModel<ContainerItem<Object>>)list.getModel();
//If the JDBCTable related to the JDBCColumn has been modified, reload the jdbcColumn values
if (jdbcColumn.isSourceModified()) {
jdbcColumn.setSourceModified(false);
model.removeAllElements();
if (dataMap.get(jdbcColumn.getJDBCTableIdentifier()) != null) {
List<ContainerItem<Object>> listContainer = populateWithColumns(jdbcColumn, dataMap);
for(ContainerItem<Object> container : listContainer){
model.addElement(container);
}
}
}
//If the comboBox doesn't contains any values, it mean that the JDBCTable hasn't been well selected.
//So show a tooltip text to warn the user.
if (model.getSize() == 0) {
list.putClientProperty(INITIAL_DELAY_PROPERTY, ToolTipManager.sharedInstance().getInitialDelay());
list.putClientProperty(TOOLTIP_TEXT_PROPERTY, list.getToolTipText());
ToolTipManager.sharedInstance().setInitialDelay(0);
ToolTipManager.sharedInstance().setDismissDelay(2500);
String jdbcTableColumnStr = jdbcColumn.getJDBCTableIdentifier().toString();
if(jdbcTableColumnStr.contains("$")){
String[] split = jdbcTableColumnStr.split("\\$");
if(split.length == 2){
jdbcTableColumnStr = split[1];
}
else if(split.length == 3){
jdbcTableColumnStr = split[1]+"."+split[2];
}
list.setToolTipText(I18N.tr("First configure the JDBCColumn {0}", jdbcTableColumnStr));
}
else {
list.setToolTipText(I18N.tr("First configure the JDBCTable {0}",
jdbcTableColumnStr.substring(jdbcTableColumnStr.lastIndexOf(":") + 1)));
}
ToolTipManager.sharedInstance().mouseMoved(
new MouseEvent(list, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
}
else{
if(dataMap.containsKey(uri) && dataMap.get(uri) != null){
List<Integer> indexList = new ArrayList<>();
String[] elements = dataMap.get(uri).toString().split("\\t");
for (int i = 0; i < Math.min(model.getSize(), elements.length); i++) {
if (model.getElementAt(i).getLabel().toUpperCase().equals(elements[i].toUpperCase())) {
indexList.add(i);
}
}
int[] indexes = new int[indexList.size()];
for(int i=0; i<indexList.size(); i++){
indexes[i] = indexList.get(i);
}
list.setSelectedIndices(indexes);
}
}
if(model.getSize()>maxRow){
list.setVisibleRowCount(maxRow);
}
else{
list.setVisibleRowCount(model.getSize());
}
list.revalidate();
list.repaint();
}
}
/**
* When an item of the comboBox is selected, save it in the data map
* @param source The source JComboBox.
*/
public void onItemSelected(Object source){
if(source instanceof JComboBox){
JComboBox<ContainerItem<Object>> comboBox = (JComboBox)source;
if(comboBox.getSelectedItem() != null) {
ContainerItem<Object> selectedItem = (ContainerItem<Object>)comboBox.getSelectedItem();
Object defaultItem = comboBox.getClientProperty(DEFAULT_ITEM_PROPERTY);
JDBCColumn jdbcColumn = (JDBCColumn) comboBox.getClientProperty(DATA_COLUMN_PROPERTY);
Map<URI, Object> dataMap = (Map<URI, Object>) comboBox.getClientProperty(DATA_MAP_PROPERTY);
URI uri = (URI) comboBox.getClientProperty(URI_PROPERTY);
boolean isOptional = (boolean)comboBox.getClientProperty(IS_OPTIONAL_PROPERTY);
dataMap.remove(uri);
if ((isOptional && selectedItem.getLabel().isEmpty()) || selectedItem.equals(defaultItem)) {
dataMap.put(uri, null);
}
else{
dataMap.put(uri, selectedItem.getLabel());
}
//Tells to the jdbcValues that the jdbcColumn has been modified
if(jdbcColumn.getJDBCValueList() != null) {
for (JDBCValue jdbcValue : jdbcColumn.getJDBCValueList()) {
jdbcValue.setJDBCColumnModified(true);
}
}
}
}
else if(source instanceof JList){
JList<ContainerItem<Object>> list = (JList<ContainerItem<Object>>)source;
ListModel<ContainerItem<Object>> model = list.getModel();
if(model.getSize()>0){
String columnList = "";
for(int i = 0; i<list.getSelectedIndices().length; i++){
if(columnList.isEmpty()){
columnList = model.getElementAt(i).getLabel();
}
else{
columnList += "\t"+model.getElementAt(i).getLabel();
}
}
Map<URI, Object> dataMap = (Map<URI, Object>) list.getClientProperty(DATA_MAP_PROPERTY);
URI uri = (URI) list.getClientProperty(URI_PROPERTY);
boolean isOptional = (boolean)list.getClientProperty(IS_OPTIONAL_PROPERTY);
dataMap.remove(uri);
if (isOptional && columnList.isEmpty()) {
dataMap.put(uri, null);
}
else{
dataMap.put(uri, columnList);
}
}
}
}
/**
* Populate the given comboBox with the table columns name list.
* Also display the columns information like if it is spatial or not, the SRID, the dimension ...
*/
private List<ContainerItem<Object>> populateWithColumns(JDBCColumn jdbcColumn, Map<URI, Object> dataMap){
//Retrieve the table name list
List<ContainerItem<Object>> listContainer = new ArrayList<>();
String tableName = null;
if(jdbcColumn.getJDBCTableIdentifier().toString().contains("$")){
String[] split = jdbcColumn.getJDBCTableIdentifier().toString().split("\\$");
if(split.length == 3){
tableName = split[1]+"."+split[2];
}
else if(split.length == 2){
tableName = split[1];
}
}
else if(dataMap.get(jdbcColumn.getJDBCTableIdentifier()) != null){
tableName = dataMap.get(jdbcColumn.getJDBCTableIdentifier()).toString();
}
if(tableName == null){
listContainer.add(new ContainerItem<Object>(I18N.tr("Select a column"), I18N.tr("Select a column")));
return listContainer;
}
List<Map<ToolboxWpsClient.JdbcProperties, Object>> informationList = wpsClient.getColumnInformation(tableName);
//If there is tables, retrieve their information to format the display in the comboBox
if(informationList != null && !informationList.isEmpty()){
for (Map<ToolboxWpsClient.JdbcProperties, Object> informationMap : informationList) {
String columnName = (String)informationMap.get(ToolboxWpsClient.JdbcProperties.COLUMN_NAME);
boolean isColumnExcluded = false;
if(jdbcColumn.getExcludedNameList() != null){
for(String excludedName : jdbcColumn.getExcludedNameList()){
if(excludedName.toLowerCase().equals(columnName.toLowerCase())){
isColumnExcluded = true;
}
}
}
if(!isColumnExcluded && jdbcColumn.getDataTypeList() != null){
String columnType = (String)informationMap.get(ToolboxWpsClient.JdbcProperties.COLUMN_TYPE);
boolean isValid = false;
for(DataType dataType : jdbcColumn.getDataTypeList()){
if(dataType.isDataTypeEquivalent(DataType.getDataType(columnType))){
isValid = true;
}
}
if(!isValid){
isColumnExcluded = true;
}
}
if(!isColumnExcluded && jdbcColumn.getExcludedTypeList() != null){
String columnType = (String)informationMap.get(ToolboxWpsClient.JdbcProperties.COLUMN_TYPE);
for(DataType excludedType : jdbcColumn.getExcludedTypeList()){
if(excludedType.isDataTypeEquivalent(DataType.getDataType(columnType))){
isColumnExcluded = true;
}
}
}
if(!isColumnExcluded){
//Retrieve the table information
//If there is information, use it to improve the table display in the comboBox
JPanel columnPanel = new JPanel(new MigLayout("ins 0, gap 0"));
if (!informationMap.isEmpty()) {
//Sets the spatial icon
String columnType = (String)informationMap.get(ToolboxWpsClient.JdbcProperties.COLUMN_TYPE);
columnPanel.add(new JLabel(ToolBoxIcon.getIcon(columnType.toLowerCase())));
columnPanel.add(new JLabel(columnName));
//Sets the SRID label
int srid = (int) informationMap.get(ToolboxWpsClient.JdbcProperties.COLUMN_SRID);
if (srid != 0) {
columnPanel.add(new JLabel(I18N.tr(" [EPSG:" + srid + "]")));
}
//Sets the dimension label
int dimension = (int) informationMap.get(ToolboxWpsClient.JdbcProperties.COLUMN_DIMENSION);
if (dimension != 2 && dimension != 0) {
columnPanel.add(new JLabel(I18N.tr(" "+dimension + "D")));
}
} else {
columnPanel.add(new JLabel(columnName));
}
listContainer.add(new ContainerItem<Object>(columnPanel, columnName));
}
}
}
return listContainer;
}
}