All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datacleaner.windows.FixedWidthDatastoreDialog Maven / Gradle / Ivy

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Neopost - Customer Information Management
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.windows;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

import javax.inject.Inject;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter;

import org.datacleaner.connection.FixedWidthDatastore;
import org.datacleaner.util.ImmutableEntry;
import org.datacleaner.util.StringUtils;
import org.datacleaner.bootstrap.WindowContext;
import org.datacleaner.guice.Nullable;
import org.datacleaner.panels.DCPanel;
import org.datacleaner.user.MutableDatastoreCatalog;
import org.datacleaner.user.UserPreferences;
import org.datacleaner.util.DCDocumentListener;
import org.datacleaner.util.FileFilters;
import org.datacleaner.util.IconUtils;
import org.datacleaner.util.NumberDocument;
import org.datacleaner.util.WidgetFactory;
import org.datacleaner.util.WidgetUtils;
import org.datacleaner.widgets.CharSetEncodingComboBox;
import org.datacleaner.widgets.DCComboBox.Listener;
import org.datacleaner.widgets.DCLabel;
import org.datacleaner.widgets.FilenameTextField;
import org.datacleaner.widgets.HeaderLineComboBox;
import org.apache.metamodel.fixedwidth.FixedWidthConfiguration;
import org.jdesktop.swingx.JXTextField;

public final class FixedWidthDatastoreDialog extends AbstractFileBasedDatastoreDialog {

	private static final long serialVersionUID = 1L;

	private final CharSetEncodingComboBox _encodingComboBox;
	private final JCheckBox _failOnInconsistenciesCheckBox;
	private final List _valueWidthTextFields;
	private final DCPanel _valueWidthsPanel;
	private final DCLabel _lineWidthLabel;
	private final HeaderLineComboBox _headerLineComboBox;
	private final JButton _addValueWidthButton;
	private final JButton _removeValueWidthButton;
	private final DocumentListener _updatePreviewTableDocumentListener;

	private volatile boolean showPreview = true;

	@Inject
	protected FixedWidthDatastoreDialog(@Nullable FixedWidthDatastore originalDatastore,
			MutableDatastoreCatalog mutableDatastoreCatalog, WindowContext windowContext, UserPreferences userPreferences) {
		super(originalDatastore, mutableDatastoreCatalog, windowContext, userPreferences);
		_updatePreviewTableDocumentListener = new DCDocumentListener() {
			@Override
			protected void onChange(DocumentEvent event) {
				onSettingsUpdated(false);
			}
		};
		_lineWidthLabel = DCLabel.bright("");
		_valueWidthsPanel = new DCPanel();
		_valueWidthsPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0));
		_valueWidthTextFields = new ArrayList();
		_encodingComboBox = new CharSetEncodingComboBox();
		_addValueWidthButton = WidgetFactory.createSmallButton(IconUtils.ACTION_ADD);
		_removeValueWidthButton = WidgetFactory.createSmallButton(IconUtils.ACTION_REMOVE);

		_headerLineComboBox = new HeaderLineComboBox();

		_failOnInconsistenciesCheckBox = new JCheckBox("Fail on inconsistent line length", true);
		_failOnInconsistenciesCheckBox.setOpaque(false);
		_failOnInconsistenciesCheckBox.setForeground(WidgetUtils.BG_COLOR_BRIGHTEST);

		if (originalDatastore != null) {
			_encodingComboBox.setSelectedItem(originalDatastore.getEncoding());
			_failOnInconsistenciesCheckBox.setSelected(originalDatastore.isFailOnInconsistencies());

			int[] valueWidths = originalDatastore.getValueWidths();
			for (int valueWidth : valueWidths) {
				addValueWidthTextField(valueWidth);
			}

			_headerLineComboBox.setSelectedIndex(originalDatastore.getHeaderLineNumber());

			onSettingsUpdated(false);
		} else {
			addValueWidthTextField();
			addValueWidthTextField();
			addValueWidthTextField();
		}

		_addValueWidthButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				addValueWidthTextField();
			}
		});

		_removeValueWidthButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				removeValueWidthTextField();
			}
		});
		_encodingComboBox.addListener(new Listener() {
			@Override
			public void onItemSelected(String item) {
				onSettingsUpdated(false);
			}
		});
		_headerLineComboBox.addListener(new Listener() {
			@Override
			public void onItemSelected(Integer item) {
				onSettingsUpdated(false);
			}
		});
	}

	@Override
	protected boolean validateForm() {
		Object selectedEncoding = _encodingComboBox.getSelectedItem();
		if (selectedEncoding == null || selectedEncoding.toString().length() == 0) {
			setStatusError("Please select a character encoding!");
			return false;
		}
		return super.validateForm();
	}

	@Override
	protected void onFileSelected(File file) {
		onSettingsUpdated(true);
	}

	private void onSettingsUpdated(boolean autoDetectEncoding) {
		if (!validateForm()) {
			return;
		}

		byte[] sampleBuffer = getSampleBuffer();
		if (sampleBuffer == null || sampleBuffer.length == 0) {
			logger.debug("No bytes read to autodetect settings");
			return;
		}

		final String charSet;
		if (autoDetectEncoding) {
			charSet = _encodingComboBox.autoDetectEncoding(sampleBuffer);
		} else {
			charSet = _encodingComboBox.getSelectedItem().toString();
		}
		char[] sampleChars = readSampleBuffer(sampleBuffer, charSet);

		int lineLength = StringUtils.indexOf('\n', sampleChars);
		if (lineLength == -1) {
			setStatusWarning("No newline in first " + sampleChars.length + " chars");
			// don't show the preview if no newlines where found (it may try
			// to treat the whole file as a single row)
			showPreview = false;
		} else {
			int[] valueWidths = getValueWidths(false);
			int totalMappedWidth = 0;
			for (int valueWidth : valueWidths) {
				totalMappedWidth += valueWidth;
			}
			_lineWidthLabel.setText(lineLength + " chars in first line. " + totalMappedWidth + " mapped.");
			_lineWidthLabel.updateUI();
			showPreview = true;
			validateAndUpdate();
		}
	}

	@Override
	protected FixedWidthDatastore getPreviewDatastore(String filename) {
		return createDatastore("Preview", filename, false);
	}

	@Override
	protected boolean isPreviewDataAvailable() {
		return showPreview;
	}

	@Override
	protected boolean isPreviewTableEnabled() {
		return true;
	}

	private JXTextField addValueWidthTextField() {
		return addValueWidthTextField(8);
	}

	private JXTextField addValueWidthTextField(int valueWidth) {
		JXTextField textField = WidgetFactory.createTextField();
		textField.setColumns(2);
		NumberDocument document = new NumberDocument();
		document.addDocumentListener(_updatePreviewTableDocumentListener);
		textField.setDocument(document);
		textField.setText(valueWidth + "");
		_valueWidthTextFields.add(textField);
		_valueWidthsPanel.add(textField);
		if (_valueWidthTextFields.size() > 1) {
			_removeValueWidthButton.setEnabled(true);
		}
		_valueWidthsPanel.updateUI();
		onSettingsUpdated(false);
		return textField;
	}

	private JXTextField removeValueWidthTextField() {
		if (_valueWidthTextFields.isEmpty()) {
			return null;
		}
		JXTextField textField = _valueWidthTextFields.get(_valueWidthTextFields.size() - 1);
		_valueWidthTextFields.remove(textField);
		_valueWidthsPanel.remove(textField);

		if (_valueWidthTextFields.size() == 1) {
			_removeValueWidthButton.setEnabled(false);
		} else {
			_removeValueWidthButton.setEnabled(true);
		}
		_valueWidthsPanel.updateUI();
		onSettingsUpdated(false);
		return textField;
	}

	@Override
	protected boolean isWindowResizable() {
		return true;
	}

	@Override
	protected List> getFormElements() {
		final DCPanel buttonPanel = new DCPanel();
		buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0));
		buttonPanel.add(_addValueWidthButton);
		buttonPanel.add(_removeValueWidthButton);

		final DCPanel valueWidthConfigurationPanel = new DCPanel();
		valueWidthConfigurationPanel.setLayout(new BorderLayout());
		valueWidthConfigurationPanel.add(_valueWidthsPanel, BorderLayout.CENTER);
		valueWidthConfigurationPanel.add(buttonPanel, BorderLayout.EAST);
		valueWidthConfigurationPanel.add(_lineWidthLabel, BorderLayout.SOUTH);

		final List> result = super.getFormElements();
		result.add(new ImmutableEntry("Character encoding", _encodingComboBox));
		result.add(new ImmutableEntry("Column widths", valueWidthConfigurationPanel));
		result.add(new ImmutableEntry("Header line", _headerLineComboBox));
		result.add(new ImmutableEntry("", _failOnInconsistenciesCheckBox));
		return result;
	}

	@Override
	protected void setFileFilters(FilenameTextField filenameField) {
		FileFilter combinedFilter = FileFilters.combined("Any text or data file (.txt, .dat)", FileFilters.TXT,
				FileFilters.DAT);
		filenameField.addChoosableFileFilter(combinedFilter);
		filenameField.addChoosableFileFilter(FileFilters.TXT);
		filenameField.addChoosableFileFilter(FileFilters.DAT);
		filenameField.setSelectedFileFilter(combinedFilter);
	}

	@Override
	protected String getBannerTitle() {
		return "Fixed width file";
	}

	@Override
	public String getWindowTitle() {
		return "Fixed width file | Datastore";
	}

	@Override
	protected FixedWidthDatastore createDatastore(String name, String filename) {
		boolean failOnInconsistencies = _failOnInconsistenciesCheckBox.isSelected();
		return createDatastore(name, filename, failOnInconsistencies);
	}

	private FixedWidthDatastore createDatastore(String name, String filename, boolean failOnInconsistencies) {
		int[] valueWidths = getValueWidths(true);
		try {
			return new FixedWidthDatastore(name, filename, _encodingComboBox.getSelectedItem().toString(), valueWidths,
					failOnInconsistencies, getHeaderLine());
		} catch (NumberFormatException e) {
			throw new IllegalStateException("Value width must be a valid number.");
		}
	}

	private int[] getValueWidths(boolean failOnMissingValue) {
		int[] valueWidths = new int[_valueWidthTextFields.size()];
		for (int i = 0; i < valueWidths.length; i++) {
			String text = _valueWidthTextFields.get(i).getText();
			if (StringUtils.isNullOrEmpty(text)) {
				if (failOnMissingValue) {
					throw new IllegalStateException("Please fill out all column widths");
				} else {
					text = "0";
				}
			}
			valueWidths[i] = Integer.parseInt(text);
		}
		return valueWidths;
	}

	@Override
	protected String getDatastoreIconPath() {
		return IconUtils.FIXEDWIDTH_IMAGEPATH;
	}

	public int getHeaderLine() {
		Number headerLineComboValue = _headerLineComboBox.getSelectedItem();
		if (headerLineComboValue != null) {
			int intComboValue = headerLineComboValue.intValue();
			if (intComboValue < 0) {
				return FixedWidthConfiguration.NO_COLUMN_NAME_LINE;
			} else {
				// MetaModel's headerline number is 0-based
				return intComboValue;
			}
		} else {
			return FixedWidthConfiguration.DEFAULT_COLUMN_NAME_LINE;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy