package org.pentaho.platform.dataaccess.datasource.wizard.sources.csv; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; import org.pentaho.platform.dataaccess.datasource.Delimiter; import org.pentaho.platform.dataaccess.datasource.Enclosure; import org.pentaho.platform.dataaccess.datasource.wizard.AbstractWizardStep; import org.pentaho.platform.dataaccess.datasource.wizard.controllers.MessageHandler; import org.pentaho.platform.dataaccess.datasource.wizard.models.CsvFileInfo; import org.pentaho.platform.dataaccess.datasource.wizard.models.DatasourceModel; import org.pentaho.platform.dataaccess.datasource.wizard.models.IModelInfoValidationListener; import org.pentaho.platform.dataaccess.datasource.wizard.service.gwt.ICsvDatasourceServiceAsync; import org.pentaho.ui.xul.XulComponent; import org.pentaho.ui.xul.binding.Binding; import org.pentaho.ui.xul.binding.BindingConvertor; import org.pentaho.ui.xul.components.XulImage; 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.containers.XulRow; import org.pentaho.ui.xul.containers.XulTree; import org.pentaho.ui.xul.stereotype.Bindable; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; import java.util.Collection; import java.util.List; /** * User: nbaker * Date: 3/22/11 */ public class CsvPhysicalStep extends AbstractWizardStep { public static final int DEFAULT_CSV_TABLE_ROW_COUNT = 7; private XulMenuList<String> encodingTypeMenuList = null; private static final List<String> ENCODINGS = Arrays.asList("", "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "Shift_JIS", "ISO-2022-JP", "ISO-2022-CN", "ISO-2022-KR", "GB18030", "Big5", "EUC-JP", "EUC-KR", "ISO-8859-1", "ISO-8859-2", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "windows-1251", "windows-1256", "KOI8-R", "ISO-8859-9"); private XulTextbox uploadedFileTextBox; private XulTree csvDataTable; private XulLabel csvTextPreview; private DatasourceModel datasourceModel; private ICsvDatasourceServiceAsync csvDatasourceService; public CsvPhysicalStep(DatasourceModel datasourceModel, CsvDatasource parentDatasource, ICsvDatasourceServiceAsync csvDatasourceService){ super(parentDatasource); this.datasourceModel = datasourceModel; this.csvDatasourceService = csvDatasourceService; } @Override public void activating() { parentDatasource.setFinishable(false); // This step takes the place of the first. We'll grab references to it's elements added to the left stepRow = (XulRow) document.getElementById(STEP_ROWS_ID).getFirstChild(); stepImage = (XulImage) stepRow.getFirstChild(); stepLabel = (XulLabel) stepRow.getChildNodes().get(1); } @Override public void deactivate() { } @Override public XulComponent getUIComponent() { return document.getElementById("csvDeckPanel"); } @Override public String getStepName() { return "CSV Physical"; } @Override public void setBindings() { csvDataTable = (XulTree) document.getElementById("csvDataTable");//$NON-NLS-1$ uploadedFileTextBox = (XulTextbox) document.getElementById("uploadedFile"); //$NON-NLS-1$ csvTextPreview = (XulLabel) document.getElementById("csvTextPreview"); //$NON-NLS-1$ encodingTypeMenuList = (XulMenuList<String>) document.getElementById("encodingTypeMenuList"); encodingTypeMenuList.setElements(ENCODINGS); BindingConvertor<Integer, Boolean> isFirstRowHeaderConverter = BindingConvertor.integer2Boolean(); bf.createBinding( datasourceModel.getModelInfo().getFileInfo(), CsvFileInfo.HEADER_ROWS_ATTRIBUTE, "isHeaderCheckBox", //$NON-NLS-1$ "checked", //$NON-NLS-1$ isFirstRowHeaderConverter); // Binding convertor to between Delimiter and radio group selected value BindingConvertor<String, String> delimiterBindingConvertor = new BindingConvertor<String, String>() { public String sourceToTarget(String source) { Delimiter delimiter = Delimiter.lookupValue(source); if (delimiter != null) { return Delimiter.lookupValue(source).getName(); } else { return source; } } public String targetToSource(String target) { Delimiter delimiter = Delimiter.lookupName(target); if (delimiter != null) { return delimiter.getValue(); } else { return target; } } }; // add binding for the Delimiter to it's corresponding radio group bf.createBinding( datasourceModel.getModelInfo().getFileInfo(), CsvFileInfo.DELIMITER_ATTRIBUTE, "delimiterRadioGroup", //$NON-NLS-1$ "value", //$NON-NLS-1$ delimiterBindingConvertor); // Binding convertor to between Enclosure and radio group selected value BindingConvertor<String, String> enclosureBindingConvertor = new BindingConvertor<String, String>() { public String sourceToTarget(String source) { Enclosure e = Enclosure.lookupValue(source); if (e == null) { e = Enclosure.NONE; } return e.getName(); } public String targetToSource(String target) { Enclosure e = Enclosure.lookupName(target); if (e == Enclosure.NONE) { return null; } else { return e.getValue(); } } }; // add binding for the Enclosure to it's corresponding radio group bf.createBinding(datasourceModel.getModelInfo().getFileInfo(), CsvFileInfo.ENCLOSURE_ATTRIBUTE, "enclosureRadioGroup", //$NON-NLS-1$ "value", //$NON-NLS-1$ enclosureBindingConvertor); // when the delimiter changes, we need to refresh the preview datasourceModel.getModelInfo().getFileInfo().addPropertyChangeListener(CsvFileInfo.DELIMITER_ATTRIBUTE, new RefreshPreviewPropertyChangeListener()); // when the enclosure changes, we need to refresh the preview datasourceModel.getModelInfo().getFileInfo().addPropertyChangeListener(CsvFileInfo.ENCLOSURE_ATTRIBUTE, new RefreshPreviewPropertyChangeListener()); // when the first-row-is-header flag changes, we need to refresh the preview datasourceModel.getModelInfo().getFileInfo().addPropertyChangeListener(CsvFileInfo.HEADER_ROWS_ATTRIBUTE, new RefreshPreviewPropertyChangeListener()); uploadedFileTextBox.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("value")) { // set the defaults if none already selected String delimiter = datasourceModel.getModelInfo().getFileInfo().getDelimiter(); if (delimiter == null || delimiter.equals("")) { datasourceModel.getModelInfo().getFileInfo().setDelimiter(","); datasourceModel.getModelInfo().getFileInfo().setHeaderRows(1); } String enclosure = datasourceModel.getModelInfo().getFileInfo().getEnclosure(); if (enclosure == null || enclosure.equals("")) { datasourceModel.getModelInfo().getFileInfo().setEnclosure("\""); } syncModelInfo(); datasourceModel.getGuiStateModel().setDirty(true); datasourceModel.getModelInfo().validate(); } } }); bf.setBindingType(Binding.Type.ONE_WAY); // binding to set the first-row-is-header checkbox's enabled property based on the selectedItem in the filesList bf.createBinding(uploadedFileTextBox, "value", "isHeaderCheckBox", "!disabled", BindingConvertor.object2Boolean()); // binding to set the delimiters enabled property based on the selectedItem in the filesList bf.createBinding(uploadedFileTextBox, "value", "delimiterRadioGroup", "!disabled", BindingConvertor.object2Boolean()); // binding to set the enclosures enabled property based on the selectedItem in the filesList bf.createBinding(uploadedFileTextBox, "value", "enclosureRadioGroup", "!disabled", BindingConvertor.object2Boolean()); bf.createBinding(datasourceModel.getModelInfo().getFileInfo(), "friendlyFilename", uploadedFileTextBox, "value"); //$NON-NLS-1$ //$NON-NLS-2$ bf.createBinding(uploadedFileTextBox, "value", "encodingTypeMenuList", "!disabled", BindingConvertor.object2Boolean()); BindingConvertor<String, String> encodingBindingConvertor = new BindingConvertor<String, String>() { public String sourceToTarget(String source) { return source; } public String targetToSource(String target) { Collection<String> encodings = encodingTypeMenuList.getElements(); if(target != null && !encodings.contains(target)) { encodings.add(target); encodingTypeMenuList.setElements(encodings); } return target; } }; bf.setBindingType(Binding.Type.BI_DIRECTIONAL); bf.createBinding(encodingTypeMenuList, "value", datasourceModel.getModelInfo().getFileInfo(), CsvFileInfo.ENCODING, encodingBindingConvertor); //$NON-NLS-1$ //$NON-NLS-2$ datasourceModel.getModelInfo().getFileInfo().addPropertyChangeListener(CsvFileInfo.ENCODING, new RefreshPreviewPropertyChangeListener()); } @Override public void stepActivatingReverse() { super.stepActivatingReverse(); parentDatasource.setFinishable(false); } /** * Executes when refresh of preview is required (also effective when dirty flag should be set) */ private class RefreshPreviewPropertyChangeListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { datasourceModel.getGuiStateModel().setDirty(true); try { if(evt.getPropertyName().equals(CsvFileInfo.ENCODING)) { csvDatasourceService.getPreviewRows(datasourceModel.getModelInfo().getFileInfo().getTmpFilename(), datasourceModel.getModelInfo().getFileInfo().getHeaderRows() > 0, 10, datasourceModel.getModelInfo().getFileInfo().getEncoding(), new AsyncCallback<List<String>>() { public void onSuccess(List<String> lines) { try { datasourceModel.getModelInfo().getFileInfo().setContents(lines); refreshPreview(); } catch (Exception e) { GWT.log("Had an issue refreshing the data preview", e); //$NON-NLS-1$ } } public void onFailure(Throwable th) { GWT.log(th.toString()); } }); } refreshPreview(); } catch (Exception e) { GWT.log(e.toString()); } datasourceModel.getModelInfo().validate(); datasourceModel.getModelInfo().addModelInfoValidationListener(new IModelInfoValidationListener(){ @Override public void onCsvInValid() { setValid(isValidated()); } @Override public void onCsvValid() { setValid(isValidated()); } @Override public void onModelInfoValid() { setValid(isValidated()); } @Override public void onModelInfoInvalid() { setValid(isValidated()); } }); } } private boolean isValidated(){ return datasourceModel.getModelInfo().getStageTableName()!= null && datasourceModel.getModelInfo().getStageTableName().trim().length() > 0 && datasourceModel.getModelInfo().getFileInfo() != null && datasourceModel.getModelInfo().getFileInfo().getTmpFilename() != null && datasourceModel.getModelInfo().getFileInfo().getTmpFilename().length() > 0 && datasourceModel.getModelInfo().getFileInfo().getDelimiter() != null && datasourceModel.getModelInfo().getFileInfo().getDelimiter().length() > 0; } public void syncModelInfo() { String filename = datasourceModel.getModelInfo().getFileInfo().getFilename(); String tmpFilename = datasourceModel.getModelInfo().getFileInfo().getTmpFilename(); if((filename == null) || (tmpFilename != null && !tmpFilename.startsWith(filename))) { //creating a brand new ds || editing a ds having uploaded a new file filename = tmpFilename; } else if(tmpFilename == null || tmpFilename.startsWith(filename)) { // editing a ds without uploading a new file datasourceModel.getModelInfo().getFileInfo().setTmpFilename(filename); } if (filename != null) { csvDatasourceService.getEncoding(filename, new AsyncCallback<String>() { public void onSuccess(String encoding) { datasourceModel.getModelInfo().getFileInfo().setEncodingFromServer(encoding); try { // go get the file contents of the selected file csvDatasourceService.getPreviewRows(datasourceModel.getModelInfo().getFileInfo().getTmpFilename(), datasourceModel.getModelInfo().getFileInfo().getHeaderRows() > 0, 10, encoding, new AsyncCallback<List<String>>() { public void onSuccess(List<String> lines) { try { datasourceModel.getModelInfo().getFileInfo().setContents(lines); refreshPreview(); } catch (Exception e) { GWT.log("Had an issue refreshing the data preview", e); //$NON-NLS-1$ } } public void onFailure(Throwable th) { MessageHandler.getInstance().showErrorDialog(th.getMessage()); } }); } catch (Exception e) { MessageHandler.getInstance().showErrorDialog(e.getMessage()); } } public void onFailure(Throwable th) { GWT.log("Had an issue getting the encoding type", th); //$NON-NLS-1$ } }); } else { try { refreshPreview(); } catch (Exception e) { GWT.log("Had an issue refreshing the data preview", e); //$NON-NLS-1$ } } } @Bindable public void refreshPreview() throws Exception { csvTextPreview.setValue(""); //$NON-NLS-1$ CsvFileInfo fileInfo = datasourceModel.getModelInfo().getFileInfo(); csvTextPreview.setValue(fileInfo.formatSampleContents()); } }