/* * ImportFileSettingsDialog.java * * Copyright (C) 2009-12 by RStudio, Inc. * * Unless you have received this program directly from RStudio pursuant * to the terms of a commercial license agreement with RStudio, then * this program is licensed to you under the terms of version 3 of the * GNU Affero General Public License. This program is distributed WITHOUT * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. * */ package org.rstudio.studio.client.workbench.views.environment.dataimport; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.TextAreaElement; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.*; import org.rstudio.core.client.Invalidation; import org.rstudio.core.client.Invalidation.Token; import org.rstudio.core.client.files.FileSystemItem; import org.rstudio.core.client.js.JsObject; import org.rstudio.core.client.widget.ModalDialog; import org.rstudio.core.client.widget.OperationWithInput; import org.rstudio.core.client.widget.ProgressIndicator; import org.rstudio.studio.client.common.GlobalDisplay; import org.rstudio.studio.client.server.ServerError; import org.rstudio.studio.client.server.ServerRequestCallback; import org.rstudio.studio.client.workbench.views.environment.model.DataPreviewResult; import org.rstudio.studio.client.workbench.views.environment.model.EnvironmentServerOperations; import org.rstudio.studio.client.workbench.views.source.editors.text.IconvListResult; import org.rstudio.studio.client.workbench.views.source.model.SourceServerOperations; public class ImportFileSettingsDialog extends ModalDialog<ImportFileSettingsDialogResult> { interface Resources extends ClientBundle { @Source("ImportFileSettingsDialog.css") Styles styles(); } interface Styles extends CssResource { String varname(); String nastrings(); String input(); String output(); String inputLabel(); String outputLabel(); String header(); String leftPanel(); String list(); } interface MyBinder extends UiBinder<Widget, ImportFileSettingsDialog> {} public ImportFileSettingsDialog( EnvironmentServerOperations server, SourceServerOperations sourceServer, FileSystemItem dataFile, String varname, String caption, OperationWithInput<ImportFileSettingsDialogResult> operation, GlobalDisplay globalDisplay) { super(caption, operation); server_ = server; sourceServer_ = sourceServer; dataFile_ = dataFile; globalDisplay_ = globalDisplay; Resources res = GWT.create(Resources.class); styles_ = res.styles(); MyBinder binder = GWT.create(MyBinder.class); widget_ = binder.createAndBindUi(this); if (varname != null) varname_.setText(varname); else varname_.setText(dataFile.getStem() .replace(" ", ".") .replace("-", ".")); separator_.addItem("Whitespace", ""); separator_.addItem("Comma", ","); separator_.addItem("Semicolon", ";"); separator_.addItem("Tab", "\t"); decimal_.addItem("Period", "."); decimal_.addItem("Comma", ","); quote_.addItem("Double quote (\")", "\""); quote_.addItem("Single quote (')", "'"); quote_.addItem("None", ""); comment_.addItem("None", ""); comment_.addItem("#", "#"); comment_.addItem("!", "!"); comment_.addItem("%", "%"); comment_.addItem("@", "@"); comment_.addItem("/", "/"); comment_.addItem("~", "~"); rowNames_.addItem("Automatic", autoValue); rowNames_.addItem("Use first column", "1"); rowNames_.addItem("Use numbers", "NULL"); encoding_.addItem("Automatic", "unknown"); sourceServer_.iconvlist(new ServerRequestCallback<IconvListResult>() { @Override public void onResponseReceived(IconvListResult result) { JsArrayString encodings = result.getAll(); for (int i = 0; i < encodings.length(); i++) { encoding_.addItem(encodings.get(i), encodings.get(i)); } } @Override public void onError(ServerError error) { // not fatal; we'll just leave Automatic as the only option } }); hookChangeEvents(); ((TextAreaElement) input_.getElement().cast()).setReadOnly(true); ((TextAreaElement) outputPanel_.getElement().cast()).setReadOnly(true); progress_ = addProgressIndicator(); setOkButtonCaption("Import"); } @Override protected void onLoad() { super.onLoad(); separator_.setSelectedIndex(-1); quote_.setSelectedIndex(-1); naStrings_.setText("NA"); loadData(); } private void hookChangeEvents() { ValueChangeHandler<Boolean> valueChangeHandler = new ValueChangeHandler<Boolean>() { public void onValueChange(ValueChangeEvent<Boolean> booleanValueChangeEvent) { updateOutput(); } }; headingYes_.addValueChangeHandler(valueChangeHandler); headingNo_.addValueChangeHandler(valueChangeHandler); ChangeHandler changeHandler = new ChangeHandler() { public void onChange(ChangeEvent event) { updateOutput(); } }; separator_.addChangeHandler(changeHandler); decimal_.addChangeHandler(changeHandler); quote_.addChangeHandler(changeHandler); encoding_.addChangeHandler(changeHandler); comment_.addChangeHandler(changeHandler); } private void updateOutput() { if (separator_.getSelectedIndex() < 0 || quote_.getSelectedIndex() < 0 || decimal_.getSelectedIndex() < 0) { return; } updateRequest_.invalidate(); final Token invalidationToken = updateRequest_.getInvalidationToken(); progress_.onProgress("Updating preview"); server_.getOutputPreview( dataFile_.getPath(), encoding_.getValue(encoding_.getSelectedIndex()), headingYes_.getValue().booleanValue(), separator_.getValue(separator_.getSelectedIndex()), decimal_.getValue(decimal_.getSelectedIndex()), quote_.getValue(quote_.getSelectedIndex()), comment_.getValue(comment_.getSelectedIndex()), new ServerRequestCallback<DataPreviewResult>() { @Override public void onResponseReceived(DataPreviewResult response) { if (invalidationToken.isInvalid()) return; progress_.onProgress(null); populateOutput(response); } @Override public void onError(ServerError error) { if (invalidationToken.isInvalid()) return; progress_.onProgress(null); globalDisplay_.showErrorMessage( "Error", error.getUserMessage()); } }); } private void loadData() { final Token invalidationToken = updateRequest_.getInvalidationToken(); progress_.onProgress("Detecting data format"); server_.getDataPreview( dataFile_.getPath(), new ServerRequestCallback<DataPreviewResult>() { @Override public void onResponseReceived(DataPreviewResult response) { input_.setHTML(toInputHtml(response)); if (invalidationToken.isInvalid()) return; progress_.onProgress(null); populateOutput(response); if (response.hasHeader()) headingYes_.setValue(true); else headingNo_.setValue(true); selectByValue(separator_, response.getSeparator()); selectByValue(decimal_, response.getDecimal()); selectByValue(quote_, response.getQuote()); selectByValue(comment_, response.getComment()); defaultStringsAsFactors_ = response.getDefaultStringsAsFactors(); stringsAsFactors_.setValue(defaultStringsAsFactors_); } @Override public void onError(ServerError error) { if (invalidationToken.isInvalid()) return; progress_.onProgress(null); globalDisplay_.showErrorMessage( "Error", error.getUserMessage()); } }); } private void selectByValue(ListBox listBox, String value) { for (int i = 0; i < listBox.getItemCount(); i++) { if (equal(listBox.getValue(i), value)) { listBox.setSelectedIndex(i); return; } } listBox.setSelectedIndex(-1); } private boolean equal(String v1, String v2) { if (v1 == null ^ v2 == null) return false; if (v1 == null) return true; return v1.equals(v2); } private void populateOutput(DataPreviewResult result) { JsArray<JsObject> output = result.getOutput(); JsArrayString names = result.getOutputNames(); int rows = output.length(); int cols = names.length(); Grid grid = new Grid(rows + 1, cols); grid.setCellPadding(0); grid.setCellSpacing(0); grid.getRowFormatter().addStyleName(0, styles_.header()); for (int col = 0; col < cols; col++) grid.setText(0, col, names.get(col)); for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { String val = output.get(row).getString(names.get(col), true); if (val == null) val = "NA"; grid.setText(row + 1, col, val); } } outputPanel_.setWidget(grid); } private String toInputHtml(DataPreviewResult response) { String input = response.getInputLines(); return input.replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll("^ ", " ") .replaceAll(" $", " ") .replaceAll("\\t", "<span style=\"background-color: #EEE; color: #888; padding: 0 10px 0 10px\">⇥</span>"); } @Override protected ImportFileSettingsDialogResult collectInput() { String rowNames = rowNames_.getValue(rowNames_.getSelectedIndex()); return new ImportFileSettingsDialogResult( new ImportFileSettings( dataFile_, varname_.getText().trim(), encoding_.getValue(encoding_.getSelectedIndex()), headingYes_.getValue(), rowNames.equals(autoValue) ? null : rowNames, separator_.getValue(separator_.getSelectedIndex()), decimal_.getValue(decimal_.getSelectedIndex()), quote_.getValue(quote_.getSelectedIndex()), comment_.getValue(comment_.getSelectedIndex()), naStrings_.getText().trim(), stringsAsFactors_.getValue()), defaultStringsAsFactors_); } @Override protected boolean validate(ImportFileSettingsDialogResult input) { if (varname_.getText().trim().length() == 0) { varname_.setFocus(true); globalDisplay_.showErrorMessage("Variable Name Is Required", "Please provide a variable name."); return false; } return (headingYes_.getValue() || headingNo_.getValue()) && separator_.getSelectedIndex() >= 0 && quote_.getSelectedIndex() >= 0; } @Override protected Widget createMainWidget() { return widget_; } public static void ensureStylesInjected() { Resources res = GWT.create(Resources.class); res.styles().ensureInjected(); } @UiField ListBox separator_; @UiField ListBox decimal_; @UiField ListBox quote_; @UiField HTML input_; @UiField SimplePanel outputPanel_; @UiField RadioButton headingYes_; @UiField RadioButton headingNo_; @UiField TextBox varname_; @UiField TextBox naStrings_; @UiField CheckBox stringsAsFactors_; @UiField ListBox encoding_; @UiField ListBox rowNames_; @UiField ListBox comment_; private final Widget widget_; private final EnvironmentServerOperations server_; private final FileSystemItem dataFile_; private final SourceServerOperations sourceServer_; private boolean defaultStringsAsFactors_ = true; private final GlobalDisplay globalDisplay_; private ProgressIndicator progress_; private final Invalidation updateRequest_ = new Invalidation(); private final Styles styles_; private static final String autoValue = "Auto"; }