/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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 Lesser General Public License for more details.
*
* Copyright 2008 - 2009 Pentaho Corporation. All rights reserved.
*
*
* Created June 7, 2009
* @author rmansoor
*/
package org.pentaho.platform.dataaccess.datasource.wizard.controllers;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import org.pentaho.metadata.model.Domain;
import org.pentaho.metadata.model.InlineEtlPhysicalModel;
import org.pentaho.metadata.model.concept.types.AggregationType;
import org.pentaho.platform.dataaccess.datasource.DatasourceType;
import org.pentaho.platform.dataaccess.datasource.Delimiter;
import org.pentaho.platform.dataaccess.datasource.Enclosure;
import org.pentaho.platform.dataaccess.datasource.beans.BusinessData;
import org.pentaho.platform.dataaccess.datasource.utils.ExceptionParser;
import org.pentaho.platform.dataaccess.datasource.wizard.DatasourceMessages;
import org.pentaho.platform.dataaccess.datasource.wizard.WaitingDialog;
import org.pentaho.platform.dataaccess.datasource.wizard.models.Aggregation;
import org.pentaho.platform.dataaccess.datasource.wizard.models.CsvModelDataRow;
import org.pentaho.platform.dataaccess.datasource.wizard.models.DatasourceModel;
import org.pentaho.platform.dataaccess.datasource.wizard.service.IXulAsyncDatasourceService;
import org.pentaho.ui.xul.XulServiceCallback;
import org.pentaho.ui.xul.binding.Binding;
import org.pentaho.ui.xul.binding.BindingConvertor;
import org.pentaho.ui.xul.binding.BindingFactory;
import org.pentaho.ui.xul.components.XulButton;
import org.pentaho.ui.xul.components.XulCheckbox;
import org.pentaho.ui.xul.components.XulFileUpload;
import org.pentaho.ui.xul.components.XulLabel;
import org.pentaho.ui.xul.components.XulMenuList;
import org.pentaho.ui.xul.components.XulTextbox;
import org.pentaho.ui.xul.components.XulTreeCol;
import org.pentaho.ui.xul.containers.XulDialog;
import org.pentaho.ui.xul.containers.XulTree;
import org.pentaho.ui.xul.containers.XulVbox;
import org.pentaho.ui.xul.impl.AbstractXulEventHandler;
import org.pentaho.ui.xul.stereotype.Bindable;
import org.pentaho.ui.xul.util.TreeCellEditor;
import org.pentaho.ui.xul.util.TreeCellEditorCallback;
import org.pentaho.ui.xul.util.TreeCellRenderer;
public class CsvDatasourceController extends AbstractXulEventHandler implements IDatasourceTypeController {
public static final int MAX_SAMPLE_DATA_ROWS = 5;
public static final int MAX_COL_SIZE = 13;
public static final String EMPTY_STRING = "";//$NON-NLS-1$
public static final String COMMA = ",";//$NON-NLS-1$
private DatasourceMessages datasourceMessages;
private WaitingDialog waitingDialogBox;
private IXulAsyncDatasourceService service;
private XulDialog regenerateModelConfirmationDialog = null;
private XulDialog waitingDialog = null;
private XulLabel waitingDialogLabel = null;
private DatasourceModel datasourceModel;
private BindingFactory bf;
private XulTextbox datasourceName = null;
private XulDialog errorDialog;
private XulDialog successDialog;
private XulLabel errorLabel = null;
private XulLabel successLabel = null;
private XulTree csvDataTable = null;
private XulTextbox selectedFile = null;
private XulCheckbox headersPresent = null;
private XulTreeCol columnNameTreeCol = null;
private XulTreeCol columnTypeTreeCol = null;
//private XulTreeCol columnFormatTreeCol = null;
private XulDialog aggregationEditorDialog = null;
private XulDialog sampleDataDialog = null;
private XulTree sampleDataTree = null;
private CustomAggregateCellEditor aggregationCellEditor = null;
private CustomSampleDataCellEditor sampleDataCellEditor = null;
private CustomAggregationCellRenderer aggregationCellRenderer = null;
private XulDialog applyCsvConfirmationDialog = null;
private XulVbox csvAggregationEditorVbox = null;
private CustomSampleDataCellRenderer sampleDataCellRenderer = null;
private XulMenuList delimiterList = null;
private XulMenuList enclosureList = null;
private XulFileUpload fileUpload = null;
private XulButton applyCsvButton = null;
public CsvDatasourceController() {
}
@Bindable
public void init() {
fileUpload = (XulFileUpload) document.getElementById("fileUpload"); //$NON-NLS-1$
applyCsvButton = (XulButton) document.getElementById("applyCsvButton"); //$NON-NLS-1$
csvAggregationEditorVbox = (XulVbox) document.getElementById("csvAggregationEditorVbox"); //$NON-NLS-1$
applyCsvConfirmationDialog = (XulDialog) document.getElementById("applyCsvConfirmationDialog"); //$NON-NLS-1$
csvDataTable = (XulTree) document.getElementById("csvDataTable");//$NON-NLS-1$
sampleDataTree = (XulTree) document.getElementById("csvSampleDataTable");//$NON-NLS-1$
aggregationEditorDialog = (XulDialog) document.getElementById("csvAggregationEditorDialog");//$NON-NLS-1$
aggregationCellEditor = new CustomAggregateCellEditor(aggregationEditorDialog, datasourceMessages, document, bf);
csvDataTable.registerCellEditor("aggregation-cell-editor", aggregationCellEditor);//$NON-NLS-1$
aggregationCellRenderer = new CustomAggregationCellRenderer();
csvDataTable.registerCellRenderer("aggregation-cell-editor", aggregationCellRenderer);//$NON-NLS-1$
sampleDataDialog = (XulDialog) document.getElementById("csvSampleDataDialog");//$NON-NLS-1$
sampleDataCellEditor = new CustomSampleDataCellEditor(sampleDataDialog);
csvDataTable.registerCellEditor("sample-data-cell-editor", sampleDataCellEditor);//$NON-NLS-1$
sampleDataCellRenderer = new CustomSampleDataCellRenderer();
csvDataTable.registerCellRenderer("sample-data-cell-editor", sampleDataCellRenderer);//$NON-NLS-1$
regenerateModelConfirmationDialog = (XulDialog) document.getElementById("regenerateModelConfirmationDialog"); //$NON-NLS-1$
waitingDialog = (XulDialog) document.getElementById("waitingDialog"); //$NON-NLS-1$
waitingDialogLabel = (XulLabel) document.getElementById("waitingDialogLabel");//$NON-NLS-1$
errorDialog = (XulDialog) document.getElementById("errorDialog"); //$NON-NLS-1$
errorLabel = (XulLabel) document.getElementById("errorLabel");//$NON-NLS-1$
successDialog = (XulDialog) document.getElementById("successDialog"); //$NON-NLS-1$
successLabel = (XulLabel) document.getElementById("successLabel");//$NON-NLS-1$
headersPresent = (XulCheckbox) document.getElementById("headersPresent"); //$NON-NLS-1$
datasourceName = (XulTextbox) document.getElementById("datasourcename"); //$NON-NLS-1$
selectedFile = (XulTextbox) document.getElementById("selectedFile"); //$NON-NLS-1$
columnNameTreeCol = (XulTreeCol) document.getElementById("csvColumnNameTreeCol"); //$NON-NLS-1$
columnTypeTreeCol = (XulTreeCol) document.getElementById("csvColumnTypeTreeCol"); //$NON-NLS-1$
delimiterList = (XulMenuList) document.getElementById("delimiterList"); //$NON-NLS-1$
enclosureList = (XulMenuList) document.getElementById("enclosureList"); //$NON-NLS-1$
datasourceName = (XulTextbox) document.getElementById("csvDatasourceName"); //$NON-NLS-1$
//columnFormatTreeCol = (XulTreeCol) document.getElementById("csvColumnFormatTreeCol"); //$NON-NLS-1$
bf.setBindingType(Binding.Type.BI_DIRECTIONAL);
final Binding domainBinding = bf.createBinding(datasourceModel.getCsvModel(),
"headersPresent", headersPresent, "checked"); //$NON-NLS-1$ //$NON-NLS-2$
bf.createBinding(datasourceModel.getCsvModel(), "dataRows", csvDataTable, "elements");//$NON-NLS-1$ //$NON-NLS-2$
bf.createBinding(datasourceModel.getCsvModel(), "delimiterList", delimiterList, "elements");//$NON-NLS-1$ //$NON-NLS-2$
bf.createBinding(datasourceModel.getCsvModel(), "enclosureList", enclosureList, "elements");//$NON-NLS-1$ //$NON-NLS-2$
bf.createBinding(datasourceModel.getCsvModel(), "selectedFile", fileUpload, "selectedFile");//$NON-NLS-1$ //$NON-NLS-2$
BindingConvertor<String, Boolean> buttonConverter = new BindingConvertor<String, Boolean>() {
@Override
public Boolean sourceToTarget(String value) {
return (value != null && value.length() > 0);
}
@Override
public String targetToSource(Boolean value) {
return null;
}
};
bf.createBinding(datasourceModel.getCsvModel(), "datasourceName", applyCsvButton, "!disabled", buttonConverter); //$NON-NLS-1$ //$NON-NLS-2$
BindingConvertor<Integer, Enclosure> indexToEnclosureConverter = new BindingConvertor<Integer, Enclosure>() {
@Override
public Enclosure sourceToTarget(Integer value) {
if (value == 0) {
return Enclosure.SINGLEQUOTE;
} else if (value == 1) {
return Enclosure.DOUBLEQUOTE;
}
return Enclosure.DOUBLEQUOTE;
}
@Override
public Integer targetToSource(Enclosure value) {
if (value == Enclosure.SINGLEQUOTE) {
return 0;
} else if (value == Enclosure.DOUBLEQUOTE) {
return 1;
}
return 1;
}
};
BindingConvertor<Integer, Delimiter> indexToDelimiterConverter = new BindingConvertor<Integer, Delimiter>() {
@Override
public Delimiter sourceToTarget(Integer value) {
if (value == 0) {
return Delimiter.COMMA;
} else if (value == 1) {
return Delimiter.TAB;
} else if (value == 2) {
return Delimiter.SEMICOLON;
} else if (value == 3) {
return Delimiter.SPACE;
}
return Delimiter.COMMA;
}
@Override
public Integer targetToSource(Delimiter value) {
if (value == Delimiter.COMMA) {
return 0;
} else if (value == Delimiter.TAB) {
return 1;
} else if (value == Delimiter.SEMICOLON) {
return 2;
} else if (value == Delimiter.SPACE) {
return 3;
}
return 0;
}
};
bf.createBinding(enclosureList, "selectedIndex", datasourceModel.getCsvModel(), "enclosure", //$NON-NLS-1$ //$NON-NLS-2$
indexToEnclosureConverter);
bf.createBinding(delimiterList, "selectedIndex", datasourceModel.getCsvModel(), "delimiter", //$NON-NLS-1$ //$NON-NLS-2$
indexToDelimiterConverter);
bf.setBindingType(Binding.Type.ONE_WAY);
bf.createBinding(csvDataTable, "selectedIndex", this, "selectedCsvDataRow"); //$NON-NLS-1$ //$NON-NLS-2$
try {
domainBinding.fireSourceChanged();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
datasourceModel.getCsvModel().setHeadersPresent(true);
datasourceModel.getCsvModel().setEnclosureList();
datasourceModel.getCsvModel().setDelimiterList();
datasourceModel.getCsvModel().setDelimiter(Delimiter.COMMA);
datasourceModel.getCsvModel().setEnclosure(Enclosure.DOUBLEQUOTE);
datasourceModel.getCsvModel().setSelectedFile(null);
}
@Bindable
public void setSelectedCsvDataRow(int row) {
}
public void setBindingFactory(BindingFactory bf) {
this.bf = bf;
}
public void setDatasourceModel(DatasourceModel model) {
this.datasourceModel = model;
}
public DatasourceModel getDatasourceModel() {
return this.datasourceModel;
}
public String getName() {
return "csvDatasourceController";//$NON-NLS-1$
}
public void setService(IXulAsyncDatasourceService service) {
this.service = service;
}
@Bindable
public void submitCsv() {
fileUpload.submit();
}
@Bindable
public void applyCsv() {
if (datasourceModel.getCsvModel().getBusinessData() != null) {
applyCsvConfirmationDialog.show();
} else {
generateModel();
}
}
@Bindable
public void closeApplyCsvConfirmationDialog() {
applyCsvConfirmationDialog.hide();
}
@Bindable
public void generateModel() {
if (validateIputForCsv()) {
if (applyCsvConfirmationDialog.isVisible()) {
applyCsvConfirmationDialog.hide();
}
showWaitingDialog(datasourceMessages.getString("DatasourceController.GENERATE_MODEL"), datasourceMessages //$NON-NLS-1$
.getString("DatasourceController.WAIT")); //$NON-NLS-1$
service.generateInlineEtlLogicalModel(datasourceModel.getCsvModel().getDatasourceName(), datasourceModel.getCsvModel()
.getSelectedFile(), datasourceModel.getCsvModel().isHeadersPresent(), datasourceModel.getCsvModel()
.getDelimiter().getValue(), datasourceModel.getCsvModel().getEnclosure().getValue(),
new XulServiceCallback<BusinessData>() {
public void error(String message, Throwable error) {
hideWaitingDialog();
displayErrorMessage(error);
}
public void success(BusinessData businessData) {
try {
hideWaitingDialog();
// merge any potential changes from earlier models
if (datasourceModel.getCsvModel().getBusinessData() != null) {
Domain oldDomain = datasourceModel.getCsvModel().getBusinessData().getDomain();
Domain newDomain = businessData.getDomain();
datasourceModel.copyOverMetadata(oldDomain, newDomain);
}
// Clear out the model for data
datasourceModel.getCsvModel().setBusinessData(null);
// Setting the editable property to true so that the table can be populated with correct cell types
columnNameTreeCol.setEditable(true);
columnTypeTreeCol.setEditable(true);
//columnFormatTreeCol.setEditable(true);
datasourceModel.getCsvModel().setBusinessData(businessData);
} catch (Exception xe) {
xe.printStackTrace();
}
}
});
} else {
openErrorDialog(datasourceMessages.getString("DatasourceController.ERROR_0001_MISSING_INPUTS"), //$NON-NLS-1$
getMissingInputs());
}
}
@Bindable
private boolean validateIputForCsv() {
return (datasourceModel.getCsvModel().getSelectedFile() != null
&& (datasourceModel.getCsvModel().getDatasourceName() != null && datasourceModel.getCsvModel().getDatasourceName().length() > 0));
}
private String getMissingInputs() {
StringBuffer buffer = new StringBuffer();
if (datasourceModel.getCsvModel().getSelectedFile() == null
&& datasourceModel.getCsvModel().getSelectedFile().length() <= 0) {
buffer.append(datasourceMessages.getString("datasourceDialog.FileMissing"));//$NON-NLS-1$
buffer.append(" \n");//$NON-NLS-1$
}
if (datasourceModel.getCsvModel().getDatasourceName() == null || datasourceModel.getCsvModel().getDatasourceName().length() <= 0) {
buffer.append(datasourceMessages.getString("datasourceDialog.DatasourceNameMissing"));//$NON-NLS-1$
buffer.append(" \n");//$NON-NLS-1$
}
return buffer.toString();
}
@Bindable
public void uploadSuccess(String results) {
datasourceModel.getCsvModel().setSelectedFile(results);
applyCsv();
}
@Bindable
public void uploadFailure(Throwable t) {
openErrorDialog(datasourceMessages.getString("DatasourceController.ERROR_0005_UPLOAD_FAILED"), t.getLocalizedMessage()); //$NON-NLS-1$
}
@Bindable
public void openErrorDialog(String title, String message) {
errorDialog.setTitle(title);
errorLabel.setValue(message);
errorDialog.show();
}
@Bindable
public void closeErrorDialog() {
if (!errorDialog.isHidden()) {
errorDialog.hide();
}
}
@Bindable
public void openSuccesDialog(String title, String message) {
successDialog.setTitle(title);
successLabel.setValue(message);
successDialog.show();
}
@Bindable
public void closeSuccessDialog() {
if (!successDialog.isHidden()) {
successDialog.hide();
}
}
/* public void showWaitingDialog(String title, String message) {
getWaitingDialog().setTitle(title);
getWaitingDialog().setMessage(message);
getWaitingDialog().show();
}
public void hideWaitingDialog() {
getWaitingDialog().hide();
}
*/
@Bindable
public void showWaitingDialog(String title, String message) {
waitingDialog.setTitle(title);
waitingDialogLabel.setValue(message);
waitingDialog.show();
}
@Bindable
public void hideWaitingDialog() {
waitingDialog.hide();
}
@Bindable
public void closeRegenerateModelConfirmationDialog() {
regenerateModelConfirmationDialog.hide();
}
public void displayErrorMessage(Throwable th) {
errorDialog.setTitle(ExceptionParser.getErrorHeader(th, getDatasourceMessages().getString("DatasourceEditor.USER_ERROR_TITLE"))); //$NON-NLS-1$
errorLabel.setValue(ExceptionParser.getErrorMessage(th, getDatasourceMessages().getString("DatasourceEditor.ERROR_0001_UNKNOWN_ERROR_HAS_OCCURED")));//$NON-NLS-1$
errorDialog.show();
}
/**
* @param datasourceMessages the datasourceMessages to set
*/
public void setDatasourceMessages(DatasourceMessages datasourceMessages) {
this.datasourceMessages = datasourceMessages;
}
/**
* @return the waitingDialog
*/
public WaitingDialog getWaitingDialog() {
return this.waitingDialogBox;
}
/**
* @param waitingDialog the waitingDialog to set
*/
public void setWaitingDialog(WaitingDialog waitingDialog) {
this.waitingDialogBox = waitingDialog;
}
/**
* @return the datasourceMessages
*/
public DatasourceMessages getDatasourceMessages() {
return datasourceMessages;
}
@Bindable
public void closeAggregationEditorDialog() {
aggregationCellEditor.hide();
}
@Bindable
public void saveAggregationValues() {
aggregationCellEditor.notifyListeners();
}
@Bindable
public void closeSampleDataDialog() {
sampleDataCellEditor.hide();
}
private class CustomSampleDataCellEditor implements TreeCellEditor {
XulDialog dialog = null;
TreeCellEditorCallback callback = null;
public CustomSampleDataCellEditor(XulDialog dialog) {
super();
this.dialog = dialog;
}
public Object getValue() {
// TODO Auto-generated method stub
return null;
}
public void hide() {
dialog.hide();
}
public void setValue(Object val) {
}
public void show(int row, int col, Object boundObj, String columnBinding, TreeCellEditorCallback callback) {
this.callback = callback;
CsvModelDataRow csvModelDataRow = (CsvModelDataRow) boundObj;
XulTreeCol column = sampleDataTree.getColumns().getColumn(0);
column.setLabel(csvModelDataRow.getColumnName());
List<String> values = csvModelDataRow.getSampleDataList();
List<String> sampleDataList = new ArrayList<String>();
for (int i = 1; i < MAX_SAMPLE_DATA_ROWS && i < csvModelDataRow.getSampleDataList().size(); i++) {
sampleDataList.add(values.get(i));
}
sampleDataTree.setElements(sampleDataList);
sampleDataTree.update();
dialog.setTitle(datasourceMessages.getString("DatasourceController.SAMPLE_DATA"));//$NON-NLS-1$
dialog.show();
}
}
private class CustomAggregationCellRenderer implements TreeCellRenderer {
public Object getNativeComponent() {
// TODO Auto-generated method stub
return null;
}
public String getText(Object value) {
StringBuffer buffer = new StringBuffer();
if (value instanceof Aggregation) {
Aggregation aggregation = (Aggregation) value;
List<AggregationType> aggregationList = aggregation.getAggregationList();
for (int i = 0; i < aggregationList.size(); i++) {
if (buffer.length() + datasourceMessages.getString(aggregationList.get(i).getDescription()).length() < MAX_COL_SIZE) {
buffer.append(datasourceMessages.getString(aggregationList.get(i).getDescription()));
if ((i < aggregationList.size() - 1 && (buffer.length()
+ datasourceMessages.getString(aggregationList.get(i + 1).getDescription()).length() + COMMA.length() < MAX_COL_SIZE))) {
buffer.append(COMMA);
}
} else {
break;
}
}
}
return buffer.toString();
}
public boolean supportsNativeComponent() {
// TODO Auto-generated method stub
return false;
}
}
private class CustomSampleDataCellRenderer implements TreeCellRenderer {
public Object getNativeComponent() {
// TODO Auto-generated method stub
return null;
}
public String getText(Object value) {
if (value instanceof String) {
return getSampleData((String) value);
} else if (value instanceof Vector) {
Vector<String> vectorValue = (Vector<String>) value;
StringBuffer sampleDataBuffer = new StringBuffer();
for (int i = 0; i < vectorValue.size(); i++) {
sampleDataBuffer.append(vectorValue.get(i));
}
return getSampleData(sampleDataBuffer.toString());
}
return EMPTY_STRING;
}
public boolean supportsNativeComponent() {
// TODO Auto-generated method stub
return false;
}
private String getSampleData(String sampleData) {
if (sampleData != null && sampleData.length() > 0) {
if (sampleData.length() <= MAX_COL_SIZE) {
return sampleData;
} else {
return sampleData.substring(0, MAX_COL_SIZE);
}
}
return EMPTY_STRING;
}
}
public void initializeBusinessData(BusinessData businessData) {
// modelDataTable.update();
InlineEtlPhysicalModel model = (InlineEtlPhysicalModel) businessData.getDomain().getPhysicalModels().get(0);
datasourceModel.setDatasourceType(DatasourceType.CSV);
datasourceModel.getCsvModel().setDatasourceName(businessData.getDomain().getId());
datasourceModel.getCsvModel().setDelimiter(Delimiter.lookupValue(model.getDelimiter()));
datasourceModel.getCsvModel().setEnclosure(Enclosure.lookupValue(model.getEnclosure()));
datasourceModel.getCsvModel().setHeadersPresent(model.getHeaderPresent());
// update business data
datasourceModel.getCsvModel().setBusinessData(null);
// Setting the editable property to true so that the table can be populated with correct cell types
columnNameTreeCol.setEditable(true);
columnTypeTreeCol.setEditable(true);
//columnFormatTreeCol.setEditable(true);
datasourceModel.getCsvModel().setBusinessData(businessData);
datasourceModel.onCsvModelValid();
}
public boolean supportsBusinessData(BusinessData businessData) {
return (businessData.getDomain().getPhysicalModels().get(0) instanceof InlineEtlPhysicalModel);
}
}