/**
* 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.ows._2.AllowedValues;
import net.opengis.ows._2.RangeType;
import net.opengis.ows._2.ValueType;
import net.opengis.wps._2_0.*;
import net.opengis.wps._2_0.DescriptionType;
import org.orbisgis.toolboxeditor.WpsClientImpl;
import org.orbisgis.toolboxeditor.utils.ToolBoxIcon;
import org.orbiswps.server.model.*;
import org.slf4j.LoggerFactory;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.EventHandler;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
/**
* DataUI implementation for LiteralData.
* This class generate an interactive UI dedicated to the configuration of a LiteralData.
* The interface generated will be used in the ProcessEditor.
*
* @author Sylvain PALOMINOS
**/
public class LiteralDataUI implements DataUI {
/** Size constants **/
private static final int MAX_ROW_NUMBER = 5;
private static final int MIN_ROW_NUMBER = 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_FIELD_PROPERTY = "DATA_FIELD_PROPERTY";
private static final String IS_OPTIONAL_PROPERTY = "IS_OPTIONAL_PROPERTY";
private static final String TOOLTIP_TEXT_PROPERTY = "TOOLTIP_TEXT_PROPERTY";
private static final String TYPE_PROPERTY = "TYPE_PROPERTY";
private static final String BOOLEAN_PROPERTY = "BOOLEAN_PROPERTY";
private static final String TEXT_AREA_PROPERTY = "TEXT_AREA_PROPERTY";
private static final String VERTICAL_BAR_PROPERTY = "VERTICAL_BAR_PROPERTY";
private static final String LITERAL_DATA_PROPERTY = "LITERAL_DATA_PROPERTY";
private static final String ORIENTATION_PROPERTY = "ORIENTATION_PROPERTY";
private static final String DEFAULT_VALUE_PROPERTY = "DEFAULT_VALUE_PROPERTY";
/** I18N object */
private static final I18n I18N = I18nFactory.getI18n(LiteralDataUI.class);
/** WpsClient using the generated UI. */
private WpsClientImpl wpsClient;
@Override
public void setWpsClient(WpsClientImpl wpsClient){
this.wpsClient = wpsClient;
}
@Override
public Map<URI, Object> getDefaultValue(DescriptionType inputOrOutput) {
Map<URI, Object> uriDefaultValueMap = new HashMap<>();
DataDescriptionType dataDescription = null;
URI identifier = URI.create(inputOrOutput.getIdentifier().getValue());
//Gets the dataDescription
if(inputOrOutput instanceof InputDescriptionType){
InputDescriptionType input = (InputDescriptionType) inputOrOutput;
dataDescription = input.getDataDescription().getValue();
}
if(inputOrOutput instanceof OutputDescriptionType){
OutputDescriptionType output = (OutputDescriptionType) inputOrOutput;
dataDescription = output.getDataDescription().getValue();
}
if (dataDescription instanceof LiteralDataType) {
//Find in the dataDescription the default LiteralDataDomain an retrieve its default value
for (LiteralDataDomainType ldda : ((LiteralDataType) dataDescription).getLiteralDataDomain()) {
if (ldda.isSetDefaultValue()) {
//If the default value is a Range, get the minimum as default value
if (ldda.getDefaultValue().isSetValue()) {
uriDefaultValueMap.put(identifier, (ldda.getDefaultValue().getValue()));
}
}
}
}
return uriDefaultValueMap;
}
@Override
public ImageIcon getIconFromData(DescriptionType inputOrOutput) {
DataDescriptionType dataDescription = null;
if(inputOrOutput instanceof InputDescriptionType){
dataDescription = ((InputDescriptionType) inputOrOutput).getDataDescription().getValue();
}
if(inputOrOutput instanceof OutputDescriptionType){
dataDescription = ((OutputDescriptionType) inputOrOutput).getDataDescription().getValue();
}
if(dataDescription instanceof LiteralDataType) {
LiteralDataType ld = (LiteralDataType)dataDescription;
DataType dataType = DataType.STRING;
//TODO manage the dataType with the icons
/*if(ld.getValue() != null && ld.getValue().getDataType()!= null) {
dataType = ld.getValue().getDataType();
}*/
switch (dataType) {
case STRING:
return ToolBoxIcon.getIcon(ToolBoxIcon.STRING);
case UNSIGNED_BYTE:
case SHORT:
case LONG:
case BYTE:
case INTEGER:
case DOUBLE:
case FLOAT:
return ToolBoxIcon.getIcon(ToolBoxIcon.NUMBER);
case BOOLEAN:
return ToolBoxIcon.getIcon(ToolBoxIcon.BOOLEAN);
default:
return ToolBoxIcon.getIcon(ToolBoxIcon.UNDEFINED);
}
}
return ToolBoxIcon.getIcon(ToolBoxIcon.UNDEFINED);
}
@Override
public JComponent createUI(DescriptionType inputOrOutput, Map<URI, Object> dataMap, Orientation orientation) {
JPanel panel = new JPanel(new MigLayout("fill, ins 0, gap 0"));
//If the descriptionType is an input, add a comboBox to select the input type and according to the type,
// add a second JComponent to write the input value
if(inputOrOutput instanceof InputDescriptionType){
InputDescriptionType input = (InputDescriptionType)inputOrOutput;
LiteralDataType literalData = (LiteralDataType)input.getDataDescription().getValue();
//JComboBox with the input type
JComboBox<String> comboBox = new JComboBox<>();
comboBox.addItem(literalData.getLiteralDataDomain().get(0).getDataType().getValue());
//JPanel containing the component to set the input value
JComponent dataFieldComp = new JPanel(new MigLayout("fill, ins 0, gap 0"));
URI uri = URI.create(input.getIdentifier().getValue());
comboBox.putClientProperty(LITERAL_DATA_PROPERTY, literalData);
comboBox.putClientProperty(DATA_FIELD_PROPERTY, dataFieldComp);
comboBox.putClientProperty(URI_PROPERTY, uri);
comboBox.putClientProperty(DATA_MAP_PROPERTY, dataMap);
comboBox.putClientProperty(IS_OPTIONAL_PROPERTY, input.getMinOccurs().equals(new BigInteger("0")));
comboBox.putClientProperty(TOOLTIP_TEXT_PROPERTY, input.getAbstract().get(0).getValue());
comboBox.putClientProperty(ORIENTATION_PROPERTY, orientation);
comboBox.addActionListener(EventHandler.create(ActionListener.class, this, "onBoxChange", "source"));
comboBox.setBackground(Color.WHITE);
comboBox.setToolTipText(inputOrOutput.getAbstract().get(0).getValue());
Object defaultValueObject = null;
for(LiteralDataType.LiteralDataDomain domain : literalData.getLiteralDataDomain()){
if(domain.isDefault() && domain.getDefaultValue() != null){
defaultValueObject = domain.getDefaultValue().getValue();
}
}
dataMap.put(uri, defaultValueObject);
onBoxChange(comboBox);
if(input.getMinOccurs().equals(new BigInteger("0"))) {
dataMap.remove(uri);
}
if(comboBox.getItemCount() > 1){
panel.add(comboBox, "growx, wrap");
}
panel.add(dataFieldComp, "growx, wrap");
return panel;
}
return null;
}
/**
* Call on selecting the type of data to use.
* For each type registered in the JComboBox adapts the jdbcTableField panel.
* Also add a listener to save the data value set by the user
* @param source The comboBox containing the data type to use.
*/
public void onBoxChange(Object source){
JComboBox comboBox = (JComboBox) source;
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);
LiteralDataType literalData = (LiteralDataType)comboBox.getClientProperty(LITERAL_DATA_PROPERTY);
String tooltip = (String)comboBox.getClientProperty(TOOLTIP_TEXT_PROPERTY);
Orientation orientation = (Orientation)comboBox.getClientProperty(ORIENTATION_PROPERTY);
String s = (String) comboBox.getSelectedItem();
JComponent dataComponent;
switch(DataType.valueOf(s.toUpperCase())) {
case BOOLEAN:
//Instantiate the component
dataComponent = new JPanel(new MigLayout("ins 0, gap 0"));
JRadioButton falseButton = new JRadioButton(I18N.tr("FALSE"));
JRadioButton trueButton = new JRadioButton(I18N.tr("TRUE"));
falseButton.setToolTipText(comboBox.getClientProperty(TOOLTIP_TEXT_PROPERTY).toString());
falseButton.setToolTipText(tooltip);
trueButton.setToolTipText(comboBox.getClientProperty(TOOLTIP_TEXT_PROPERTY).toString());
trueButton.setToolTipText(tooltip);
ButtonGroup group = new ButtonGroup();
group.add(falseButton);
group.add(trueButton);
dataComponent.add(trueButton);
dataComponent.add(falseButton);
//Put the data type, the dataMap and the uri as properties
falseButton.putClientProperty(TYPE_PROPERTY, DataType.BOOLEAN);
falseButton.putClientProperty(DATA_MAP_PROPERTY, dataMap);
falseButton.putClientProperty(URI_PROPERTY, uri);
falseButton.putClientProperty(BOOLEAN_PROPERTY, false);
trueButton.putClientProperty(TYPE_PROPERTY, DataType.BOOLEAN);
trueButton.putClientProperty(DATA_MAP_PROPERTY, dataMap);
trueButton.putClientProperty(URI_PROPERTY, uri);
trueButton.putClientProperty(BOOLEAN_PROPERTY, true);
dataComponent.putClientProperty(TYPE_PROPERTY, DataType.BOOLEAN);
dataComponent.putClientProperty(DATA_MAP_PROPERTY, dataMap);
dataComponent.putClientProperty(URI_PROPERTY, uri);
//Set the default value and adds the listener for saving the value set by the user
falseButton.setSelected(true);
if (dataMap.get(uri) != null) {
if (Boolean.parseBoolean(dataMap.get(uri).toString())) {
trueButton.setSelected(true);
}
dataComponent.putClientProperty(BOOLEAN_PROPERTY, dataMap.get(uri));
}
else{
dataComponent.putClientProperty(BOOLEAN_PROPERTY, false);
}
falseButton.addActionListener(EventHandler.create(
ActionListener.class,
this,
"onDataChanged",
"source"));
trueButton.addActionListener(EventHandler.create(
ActionListener.class,
this,
"onDataChanged",
"source"));
onDataChanged(dataComponent);
break;
case BYTE:
dataComponent = new JPanel(new MigLayout("fill"));
//Instantiate the component
JSpinner byteSpinner = new JSpinner(new SpinnerNumberModel(0, Byte.MIN_VALUE, Byte.MAX_VALUE, 1));
//Put the data type, the dataMap and the uri as properties
byteSpinner.putClientProperty(TYPE_PROPERTY, DataType.BYTE);
byteSpinner.putClientProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
byteSpinner.putClientProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
byteSpinner.setToolTipText(tooltip);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null && !dataMap.get(uri).toString().isEmpty()) {
byteSpinner.setValue(Byte.parseByte(dataMap.get(uri).toString()));
} else {
byteSpinner.setValue(0);
}
byteSpinner.addChangeListener(EventHandler.create(
ChangeListener.class,
this,
"onDataChanged",
"source"));
//If there is more than one literal data domain, adds a JComboBox to allow the user to select the
// desired one.
JComboBox allowedValuesBox = createDomainComboBox(literalData);
if (allowedValuesBox != null && allowedValuesBox.getItemCount() > 0) {
allowedValuesBox.putClientProperty("spinner", byteSpinner);
allowedValuesBox.addItemListener(
EventHandler.create(ItemListener.class, this, "onDomainSelected", ""));
dataComponent.add(allowedValuesBox, "dock west");
//Run a false event to simulated the selection of the selected value of the JComboBox
ItemEvent event = new ItemEvent(
allowedValuesBox, 0, allowedValuesBox.getSelectedItem(), ItemEvent.SELECTED);
onDomainSelected(event);
}
dataComponent.add(byteSpinner, "growx, wrap");
onDataChanged(byteSpinner);
break;
case INTEGER:
dataComponent = new JPanel(new MigLayout("fill"));
//Instantiate the component
JSpinner intSpinner = new JSpinner(new SpinnerNumberModel(0, Integer.MIN_VALUE, Integer.MAX_VALUE, 1));
//Put the data type, the dataMap and the uri as properties
intSpinner.putClientProperty(TYPE_PROPERTY, DataType.INTEGER);
intSpinner.putClientProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
intSpinner.putClientProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
intSpinner.setToolTipText(tooltip);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null && !dataMap.get(uri).toString().isEmpty()) {
intSpinner.setValue(Integer.parseInt(dataMap.get(uri).toString()));
} else {
intSpinner.setValue(0);
}
intSpinner.addChangeListener(EventHandler.create(
ChangeListener.class,
this,
"onDataChanged",
"source"));
//If there is more than one literal data domain, adds a JComboBox to allow the user to select the
// desired one.
allowedValuesBox = createDomainComboBox(literalData);
if (allowedValuesBox != null && allowedValuesBox.getItemCount() > 1) {
allowedValuesBox.putClientProperty("spinner", intSpinner);
allowedValuesBox.addItemListener(
EventHandler.create(ItemListener.class, this, "onDomainSelected", ""));
dataComponent.add(allowedValuesBox, "dock west");
//Run a false event to simulated the selection of the selected value of the JComboBox
ItemEvent event = new ItemEvent(
allowedValuesBox, 0, allowedValuesBox.getSelectedItem(), ItemEvent.SELECTED);
onDomainSelected(event);
}
dataComponent.add(intSpinner, "growx, span");
onDataChanged(intSpinner);
break;
case LONG:
dataComponent = new JPanel(new MigLayout("fill"));
//Instantiate the component
JSpinner longSpinner = new JSpinner(new SpinnerNumberModel(0, Long.MIN_VALUE, Long.MAX_VALUE, 1));
//Put the data type, the dataMap and the uri as properties
longSpinner.putClientProperty(TYPE_PROPERTY, DataType.LONG);
longSpinner.putClientProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
longSpinner.putClientProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
longSpinner.setToolTipText(tooltip);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null && !dataMap.get(uri).toString().isEmpty()) {
longSpinner.setValue(Long.parseLong(dataMap.get(uri).toString()));
} else {
longSpinner.setValue(0);
}
longSpinner.addChangeListener(EventHandler.create(
ChangeListener.class,
this,
"onDataChanged",
"source"));
//If there is more than one literal data domain, adds a JComboBox to allow the user to select the
// desired one.
allowedValuesBox = createDomainComboBox(literalData);
if (allowedValuesBox != null && allowedValuesBox.getItemCount() > 1) {
allowedValuesBox.putClientProperty("spinner", longSpinner);
allowedValuesBox.addItemListener(
EventHandler.create(ItemListener.class, this, "onDomainSelected", ""));
dataComponent.add(allowedValuesBox, "dock west");
//Run a false event to simulated the selection of the selected value of the JComboBox
ItemEvent event = new ItemEvent(
allowedValuesBox, 0, allowedValuesBox.getSelectedItem(), ItemEvent.SELECTED);
onDomainSelected(event);
}
dataComponent.add(longSpinner, "growx, wrap");
onDataChanged(longSpinner);
break;
case SHORT:
dataComponent = new JPanel(new MigLayout("fill"));
//Instantiate the component
JSpinner shortSpinner = new JSpinner(new SpinnerNumberModel(0, Short.MIN_VALUE, Short.MAX_VALUE, 1));
//Put the data type, the dataMap and the uri as properties
shortSpinner.putClientProperty(TYPE_PROPERTY, DataType.SHORT);
shortSpinner.putClientProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
shortSpinner.putClientProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
shortSpinner.setToolTipText(tooltip);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null && !dataMap.get(uri).toString().isEmpty()) {
shortSpinner.setValue(Short.parseShort(dataMap.get(uri).toString()));
} else {
shortSpinner.setValue(0);
}
shortSpinner.addChangeListener(EventHandler.create(
ChangeListener.class,
this,
"onDataChanged",
"source"));
//If there is more than one literal data domain, adds a JComboBox to allow the user to select the
// desired one.
allowedValuesBox = createDomainComboBox(literalData);
if (allowedValuesBox != null && allowedValuesBox.getItemCount() > 1) {
allowedValuesBox.putClientProperty("spinner", shortSpinner);
allowedValuesBox.addItemListener(
EventHandler.create(ItemListener.class, this, "onDomainSelected", ""));
dataComponent.add(allowedValuesBox, "dock west");
//Run a false event to simulated the selection of the selected value of the JComboBox
ItemEvent event = new ItemEvent(
allowedValuesBox, 0, allowedValuesBox.getSelectedItem(), ItemEvent.SELECTED);
onDomainSelected(event);
}
dataComponent.add(shortSpinner, "growx, wrap");
onDataChanged(shortSpinner);
break;
case UNSIGNED_BYTE:
dataComponent = new JPanel(new MigLayout("fill"));
//Instantiate the component
JSpinner uByteSpinner = new JSpinner(new SpinnerNumberModel(0, 0, Character.MAX_VALUE, 1));
//Put the data type, the dataMap and the uri as properties
uByteSpinner.putClientProperty(TYPE_PROPERTY, DataType.UNSIGNED_BYTE);
uByteSpinner.putClientProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
uByteSpinner.putClientProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
uByteSpinner.setToolTipText(tooltip);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null && !dataMap.get(uri).toString().isEmpty()) {
uByteSpinner.setValue(dataMap.get(uri).toString().charAt(0));
} else {
uByteSpinner.setValue(0);
}
uByteSpinner.addChangeListener(EventHandler.create(
ChangeListener.class,
this,
"onDataChanged",
"source"));
//If there is more than one literal data domain, adds a JComboBox to allow the user to select the
// desired one.
allowedValuesBox = createDomainComboBox(literalData);
if (allowedValuesBox != null && allowedValuesBox.getItemCount() > 1) {
allowedValuesBox.putClientProperty("spinner", uByteSpinner);
allowedValuesBox.addItemListener(
EventHandler.create(ItemListener.class, this, "onDomainSelected", ""));
dataComponent.add(allowedValuesBox, "dock west");
//Run a false event to simulated the selection of the selected value of the JComboBox
ItemEvent event = new ItemEvent(
allowedValuesBox, 0, allowedValuesBox.getSelectedItem(), ItemEvent.SELECTED);
onDomainSelected(event);
}
dataComponent.add(uByteSpinner, "growx, wrap");
onDataChanged(uByteSpinner);
break;
case DOUBLE:
dataComponent = new JPanel(new MigLayout("fill"));
//Instantiate the component
JSpinner doubleSpinner = new JSpinner(new SpinnerNumberModel(0D, Integer.MIN_VALUE, Integer.MAX_VALUE, 0.1));
//Put the data type, the dataMap and the uri as properties
doubleSpinner.putClientProperty(TYPE_PROPERTY, DataType.DOUBLE);
doubleSpinner.putClientProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
doubleSpinner.putClientProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
doubleSpinner.setToolTipText(tooltip);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null && !dataMap.get(uri).toString().isEmpty()) {
doubleSpinner.setValue(Double.parseDouble(dataMap.get(uri).toString()));
} else {
doubleSpinner.setValue(0);
}
doubleSpinner.addChangeListener(EventHandler.create(
ChangeListener.class,
this,
"onDataChanged",
"source"));
//If there is more than one literal data domain, adds a JComboBox to allow the user to select the
// desired one.
allowedValuesBox = createDomainComboBox(literalData);
if (allowedValuesBox != null && allowedValuesBox.getItemCount() > 1) {
allowedValuesBox.putClientProperty("spinner", doubleSpinner);
allowedValuesBox.addItemListener(
EventHandler.create(ItemListener.class, this, "onDomainSelected", ""));
dataComponent.add(allowedValuesBox, "dock west");
//Run a false event to simulated the selection of the selected value of the JComboBox
ItemEvent event = new ItemEvent(
allowedValuesBox, 0, allowedValuesBox.getSelectedItem(), ItemEvent.SELECTED);
onDomainSelected(event);
}
dataComponent.add(doubleSpinner, "growx, wrap");
onDataChanged(doubleSpinner);
break;
case FLOAT:
dataComponent = new JPanel(new MigLayout("fill"));
//Instantiate the component
JSpinner floatSpinner = new JSpinner(new SpinnerNumberModel(0F, Float.MIN_VALUE, Float.MAX_VALUE, 1));
//Put the data type, the dataMap and the uri as properties
floatSpinner.putClientProperty(TYPE_PROPERTY, DataType.FLOAT);
floatSpinner.putClientProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
floatSpinner.putClientProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
floatSpinner.setToolTipText(tooltip);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null && !dataMap.get(uri).toString().isEmpty()) {
floatSpinner.setValue(Float.parseFloat(dataMap.get(uri).toString()));
} else {
floatSpinner.setValue(0);
}
floatSpinner.addChangeListener(EventHandler.create(
ChangeListener.class,
this,
"onDataChanged",
"source"));
//If there is more than one literal data domain, adds a JComboBox to allow the user to select the
// desired one.
allowedValuesBox = createDomainComboBox(literalData);
if (allowedValuesBox != null && allowedValuesBox.getItemCount() > 1) {
allowedValuesBox.putClientProperty("spinner", floatSpinner);
allowedValuesBox.addItemListener(
EventHandler.create(ItemListener.class, this, "onDomainSelected", ""));
dataComponent.add(allowedValuesBox, "dock west");
//Run a false event to simulated the selection of the selected value of the JComboBox
ItemEvent event = new ItemEvent(
allowedValuesBox, 0, allowedValuesBox.getSelectedItem(), ItemEvent.SELECTED);
onDomainSelected(event);
}
dataComponent.add(floatSpinner, "growx, wrap");
onDataChanged(floatSpinner);
break;
case STRING:
default:
//Instantiate the component
CustomTextArea textArea = new CustomTextArea();
textArea.setToolTipText(tooltip);
textArea.setLineWrap(true);
textArea.setRows(MIN_ROW_NUMBER);
//Put the data type, the dataMap and the uri as properties
Document doc = textArea.getDocument();
doc.putProperty(DATA_MAP_PROPERTY, comboBox.getClientProperty(DATA_MAP_PROPERTY));
doc.putProperty(URI_PROPERTY, comboBox.getClientProperty(URI_PROPERTY));
doc.putProperty(IS_OPTIONAL_PROPERTY, isOptional);
String defaultValue = "";
for(LiteralDataType.LiteralDataDomain domain : literalData.getLiteralDataDomain()){
if(domain.isDefault() && domain.getDefaultValue() != null && domain.getDefaultValue().getValue() != null){
defaultValue = domain.getDefaultValue().getValue();
}
}
doc.putProperty(DEFAULT_VALUE_PROPERTY, defaultValue);
//Set the default value and adds the listener for saving the value set by the user
if (dataMap.get(uri) != null) {
textArea.setText((String) dataMap.get(uri));
} else {
textArea.setText("");
}
doc.addDocumentListener(EventHandler.create(
DocumentListener.class,
this,
"onDocumentChanged",
"document",
"insertUpdate"));
doc.addDocumentListener(EventHandler.create(
DocumentListener.class,
this,
"onDocumentChanged",
"document",
"removeUpdate"));
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.getViewport().addChangeListener(EventHandler.create(
ChangeListener.class, this, "onViewportStateChange", ""));
scrollPane.getViewport().putClientProperty(TEXT_AREA_PROPERTY, textArea);
scrollPane.getViewport().putClientProperty(VERTICAL_BAR_PROPERTY, scrollPane.getVerticalScrollBar());
//scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JPanel panel = new JPanel(new MigLayout("fill"));
panel.add(scrollPane, "growx");
if (orientation.equals(Orientation.VERTICAL)){
JButton paste = new JButton(ToolBoxIcon.getIcon(ToolBoxIcon.PASTE));
paste.putClientProperty(TEXT_AREA_PROPERTY, textArea);
paste.addActionListener(EventHandler.create(ActionListener.class, this, "onPaste", ""));
paste.setBorderPainted(false);
paste.setContentAreaFilled(false);
paste.setToolTipText(I18N.tr("Paste the clipboard"));
panel.add(paste, "dock east");
}
dataComponent = panel;
break;
}
dataComponent.setToolTipText(comboBox.getClientProperty(TOOLTIP_TEXT_PROPERTY).toString());
//Adds to the jdbcTableField the dataComponent
JPanel panel = (JPanel) comboBox.getClientProperty(DATA_FIELD_PROPERTY);
panel.removeAll();
panel.add(dataComponent, "growx, wrap");
if(isOptional) {
dataMap.remove(uri);
}
}
private class CustomTextArea extends JTextArea {
@Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(1, (int)super.getPreferredScrollableViewportSize().getHeight()); }
@Override
public boolean getScrollableTracksViewportWidth() { return true; }
}
/**
* Create a JComboBox containing all the available literal data domain to allow the user to select the desired one.
* The JComboBox contains strings which are composed this way :
* - In the case of a ValueType : "value"
* - In the case of a RangeType : "min;value;max;spacing". If there is no "value" defined, uses the "min".
* @param literalData LiteralData used to build the JComboBox.
* @return A JComboBox containing the literalData domain.
*/
private JComboBox<String> createDomainComboBox(LiteralDataType literalData){
//If there is no domain, return null;
if(literalData.getLiteralDataDomain().isEmpty()) {
return null;
}
JComboBox<String> allowedValuesBox = new JComboBox<>();
//For each domain, add an item to the JComboBox
for(LiteralDataType.LiteralDataDomain literalDataDomain : literalData.getLiteralDataDomain()){
if(literalDataDomain != null){
if(literalDataDomain.getAllowedValues() != null){
//For each allowed values, if there are not empty, add the item to the JComboBox
AllowedValues allowedValues = literalDataDomain.getAllowedValues();
if(!allowedValues.getValueOrRange().isEmpty()) {
//Test each value
for(Object value : allowedValues.getValueOrRange()){
//If the value is a ValueType, add the item "value"
if(value instanceof ValueType){
String str = ((ValueType)value).getValue();
allowedValuesBox.addItem(str);
}
//If the value is a RangeType, add the item "min;value;max;spacing".
// If there is no "value" defined, uses the "min".
if(value instanceof RangeType){
RangeType range = (RangeType)value;
String defaultValue = range.getMinimumValue().getValue();
if(!literalDataDomain.getDefaultValue().getValue().isEmpty()){
defaultValue = literalDataDomain.getDefaultValue().getValue();
}
String str = range.getMinimumValue().getValue()+";"+
defaultValue+";"+
range.getMaximumValue().getValue()+";"+
range.getSpacing().getValue();
allowedValuesBox.addItem(str);
}
}
}
}
}
}
return allowedValuesBox;
}
/**
* Action done on clicking on the paste button.
* @param ae ActionEvent get on clicking on the paste button.
*/
public void onPaste(ActionEvent ae){
JTextArea textArea = ((JTextArea)((JButton)ae.getSource()).getClientProperty(TEXT_AREA_PROPERTY));
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
//odd: the Object param of getContents is not currently used
Transferable contents = clipboard.getContents(null);
boolean hasTransferableText = (contents != null) && contents.isDataFlavorSupported(DataFlavor.stringFlavor);
if (hasTransferableText) {
try {
textArea.setText((String)contents.getTransferData(DataFlavor.stringFlavor));
}
catch (UnsupportedFlavorException | IOException ignored){
}
}
}
/**
* Call when the state of the viewport of the JScrollPane of the textArea state change.
* It uses the vertical bar properties to detect when the user need more lines to write.
* @param e ChangeEvent.
*/
public void onViewportStateChange(ChangeEvent e){
JViewport vp = (JViewport)e.getSource();
JTextArea textArea = (JTextArea)vp.getClientProperty(TEXT_AREA_PROPERTY);
JScrollBar vertical = (JScrollBar)vp.getClientProperty(VERTICAL_BAR_PROPERTY);
if(textArea.getRows()<MAX_ROW_NUMBER && vertical.getValue()>0 && vertical.getMaximum()>vertical.getVisibleAmount()){
textArea.setRows(MAX_ROW_NUMBER);
}
}
/**
* Call if the TextArea for the String type is changed and save the new text in the dataMap.
* @param document TextArea document.
*/
public void onDocumentChanged(Document document){
Map<URI, Object> dataMap = (Map<URI, Object>) document.getProperty(DATA_MAP_PROPERTY);
URI uri = (URI) document.getProperty(URI_PROPERTY);
boolean isOptional = (boolean) document.getProperty(IS_OPTIONAL_PROPERTY);
String defaultValue = (String) document.getProperty(DEFAULT_VALUE_PROPERTY);
try {
String text = document.getText(0, document.getLength());
if(text .isEmpty() && isOptional && !defaultValue.isEmpty()){
text = defaultValue;
}
else if(text.isEmpty()){
text = null;
}
dataMap.put(uri, text);
} catch (BadLocationException e) {
LoggerFactory.getLogger(LiteralDataUI.class).error(e.getMessage());
dataMap.put(uri, "");
}
}
/**
* Call if the JComponent where the value is defined is changed and save the new value in the dataMap.
* @param source Source JComponent.
*/
public void onDataChanged(Object source){
Map<URI, Object> dataMap = (Map<URI, Object>) ((JComponent)source).getClientProperty(DATA_MAP_PROPERTY);
URI uri = (URI) ((JComponent)source).getClientProperty(URI_PROPERTY);
switch((DataType)((JComponent)source).getClientProperty(TYPE_PROPERTY)){
case BOOLEAN:
dataMap.put(uri, ((JComponent)source).getClientProperty(BOOLEAN_PROPERTY));
break;
case INTEGER:
case BYTE:
case LONG:
case SHORT:
case UNSIGNED_BYTE:
case DOUBLE:
case FLOAT:
JSpinner spinner = (JSpinner)source;
dataMap.put(uri, spinner.getValue());
break;
}
}
/**
* When a domain is selected, sets the JSpinner model with the min, max, spacing and value from the domein.
* @param event Event thrown when a domain is selected.
*/
public void onDomainSelected(ItemEvent event){
//Get the domain JComboBox
if(event.getSource() instanceof JComboBox){
JComboBox domainSpinner = (JComboBox)event.getSource();
Object object = domainSpinner.getClientProperty("spinner");
//Get the value JSpinner
if(object instanceof JSpinner){
JSpinner valueSpinner = (JSpinner)object;
SpinnerModel model = null;
//If there is only one value (in case of a ValueType), use it as value, min and max.
String[] parsed = event.getItem().toString().split(";");
if(parsed.length == 1){
parsed = new String[]{parsed[0],parsed[0],parsed[0],parsed[0]};
}
//According to the data type, create the model for the spinner
switch((DataType)valueSpinner.getClientProperty(TYPE_PROPERTY)){
case INTEGER:
model = new SpinnerNumberModel(
Integer.parseInt(parsed[0]),
Integer.parseInt(parsed[1]),
Integer.parseInt(parsed[2]),
Integer.parseInt(parsed[3]));
break;
case BYTE:
model = new SpinnerNumberModel(
Byte.valueOf(parsed[0]),
Byte.valueOf(parsed[1]),
Byte.valueOf(parsed[2]),
Byte.valueOf(parsed[3]));
break;
case LONG:
model = new SpinnerNumberModel(
Long.parseLong(parsed[0]),
Long.parseLong(parsed[1]),
Long.parseLong(parsed[2]),
Long.parseLong(parsed[3]));
break;
case SHORT:
model = new SpinnerNumberModel(
Short.parseShort(parsed[0]),
Short.parseShort(parsed[1]),
Short.parseShort(parsed[2]),
Short.parseShort(parsed[3]));
break;
case UNSIGNED_BYTE:
model = new SpinnerNumberModel(
parsed[0].charAt(0),
parsed[1].charAt(0),
parsed[2].charAt(0),
parsed[3].charAt(0));
break;
case DOUBLE:
model = new SpinnerNumberModel(
Double.parseDouble(parsed[0]),
Double.parseDouble(parsed[1]),
Double.parseDouble(parsed[2]),
Double.parseDouble(parsed[3]));
break;
case FLOAT:
model = new SpinnerNumberModel(
Float.parseFloat(parsed[0]),
Float.parseFloat(parsed[1]),
Float.parseFloat(parsed[2]),
Float.parseFloat(parsed[3]));
break;
}
if(model != null) {
valueSpinner.setModel(model);
}
onDataChanged(valueSpinner);
}
}
}
}