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

eu.agrosense.client.io.csv.CsvDataObject Maven / Gradle / Ivy

There is a newer version: 14.06-beta
Show newest version
/**
 * Copyright (C) 2008-2013 LimeTri. All rights reserved.
 *
 * AgroSense 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.
 *
 * There are special exceptions to the terms and conditions of the GPLv3 as it is applied to
 * this software, see the FLOSS License Exception
 * .
 *
 * AgroSense 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 AgroSense.  If not, see .
 */
package eu.agrosense.client.io.csv;

import au.com.bytecode.opencsv.CSVReader;
import eu.agrosense.api.mapping.MappableSource;
import eu.agrosense.api.mapping.SourceAttributeDescriptor;
import eu.agrosense.api.mapping.SourceRecord;
import eu.agrosense.api.mapping.SourceRecordGenerator;
import eu.agrosense.api.mapping.SourceRecordIterator;
import eu.agrosense.client.imp.ImportDataObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import org.netbeans.core.spi.multiview.MultiViewElement;
import org.netbeans.core.spi.multiview.text.MultiViewEditorElement;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.MIMEResolver;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectExistsException;
import org.openide.loaders.MultiFileLoader;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;

@Messages({
    "LBL_Csv_LOADER=Files of Csv"
})
@MIMEResolver.ExtensionRegistration(
    displayName = "#LBL_Csv_LOADER",
mimeType = CsvDataObject.MIME_TYPE,
extension = {"csv", "CSV"},
position=1)
@DataObject.Registration(
    mimeType = CsvDataObject.MIME_TYPE,
iconBase = CsvDataObject.ICON_BASE,
displayName = "#LBL_Csv_LOADER",
position = 300)
@ActionReferences({
    @ActionReference(
        path = "Loaders/text/csv/Actions",
    id =
    @ActionID(category = "System", id = "org.openide.actions.OpenAction"),
    position = 100,
    separatorAfter = 200),
    @ActionReference(
        path = "Loaders/text/csv/Actions",
    id =
    @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"),
    position = 600),
    @ActionReference(
        path = "Loaders/text/csv/Actions",
    id =
    @ActionID(category = "System", id = "org.openide.actions.RenameAction"),
    position = 700,
    separatorAfter = 800),
    @ActionReference(
        path = "Loaders/text/csv/Actions",
    id =
    @ActionID(category = "System", id = "org.openide.actions.ToolsAction"),
    position = 1300),
})
public class CsvDataObject extends ImportDataObject {
    public static final String PRIMARY_EXT = "csv";    
    public static final String MIME_TYPE = "text/csv";
    public static final String ICON_BASE = "eu/agrosense/client/io/csv/file_extension_txt.png";
    
    private static final Logger LOGGER = Logger.getLogger("eu.agrosense.client.data.csv.CsvDataObject");
    
    public static final String PROP_SEPARATOR_CHAR = "separatorChar";
    public static final String PROP_QUOTE_CHAR = "quoteChar";
    public static final String PROP_ESCAPE_CHAR = "escapeChar";
    public static final String PROP_SKIP_LINES = "skipLines";
    
    private static final String ATT_SEPARATOR_CHAR = "att_separator_char";
    private static final String ATT_QUOTE_CHAR = "att_quote_char";
    private static final String ATT_ESCAPE_CHAR = "att_escape_char";
    private static final String ATT_SKIP_LINES = "att_skip_lines";
    
    public static final char DEFAULT_SEPARATOR_CHAR = ',';
    public static final char DEFAULT_QUOTE_CHAR = '"';
    public static final char DEFAULT_ESCAPE_CHAR = '\\';
    public static final int DEFAULT_SKIP_LINES = 1; // require header line, differs from CsvReader.DEFAULT_SKIP_LINES
    
    private final CsvMappableSource mappableSource;
    private final Charset charset = StandardCharsets.UTF_8; // maybe later: make configurable via the attributesPanel
    
    
    public CsvDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
        super(pf, loader);
        registerEditor(MIME_TYPE, true);
        mappableSource = new CsvMappableSource();
        ic.add(mappableSource);   
        init();
    }
    
    private void init() {
        if (getSeparatorChar() == null) {
            CsvFileAttributes cfa = CsvFileAttributeRegistry.getInstance().find(getHeaderLine());
            if (cfa != null) {
                setSepararatorChar(cfa.getSeparatorChar());
                setQuoteChar(cfa.getQuoteChar());
                setEscapeChar(cfa.getEscapeChar());
                setSkipLines(cfa.getSkipLines());
            } else {
                setSepararatorChar(DEFAULT_SEPARATOR_CHAR);
                setQuoteChar(DEFAULT_QUOTE_CHAR);
                setEscapeChar(DEFAULT_ESCAPE_CHAR);
                setSkipLines(DEFAULT_SKIP_LINES);   
            }
        }
    }

    @Override
    protected int associateLookup() {
        return 1;
    }
    
    public Character getSeparatorChar() {
        String str = (String) getPrimaryFile().getAttribute(ATT_SEPARATOR_CHAR);
        return str == null || str.isEmpty() ? null : str.charAt(0);
    }

    void setSepararatorChar(Character ch) {
        try {
            Character old = getSeparatorChar();
            // org.openide.filesystemsXmlMapAttr.java, line 956:
            // if (value.trim().length() != 1) {
            // so can't store tab or space separator as characters...
            String str = ch == null ? null : ch.toString();
            getPrimaryFile().setAttribute(ATT_SEPARATOR_CHAR, str);
            firePropertyChange(PROP_SEPARATOR_CHAR, old, ch);
        } catch (IOException ex) {
            LOGGER.log(Level.WARNING, "could not save separator char property for csv data object {0}", getName());
        }
    }

    public Character getQuoteChar() {
        return (Character) getPrimaryFile().getAttribute(ATT_QUOTE_CHAR);
    }

    void setQuoteChar(Character ch) {
        try {
            Character old = getQuoteChar();
            getPrimaryFile().setAttribute(ATT_QUOTE_CHAR, ch);
            firePropertyChange(PROP_QUOTE_CHAR, old, ch);
        } catch (IOException ex) {
            LOGGER.log(Level.WARNING, "could not save quote char property for csv data object {0}", getName());
        }
    }

    public Character getEscapeChar() {
        return (Character) getPrimaryFile().getAttribute(ATT_ESCAPE_CHAR);
    }

    void setEscapeChar(Character ch) {
        try {
            Character old = getEscapeChar();
            getPrimaryFile().setAttribute(ATT_ESCAPE_CHAR, ch);
            firePropertyChange(PROP_ESCAPE_CHAR, old, ch);
        } catch (IOException ex) {
            LOGGER.log(Level.WARNING, "could not save escape char property for csv data object {0}", getName());
        }
    }

    public Integer getSkipLines() {
        return (Integer) getPrimaryFile().getAttribute(ATT_SKIP_LINES);
    }

    void setSkipLines(Integer i) {
        try {
            Integer old = getSkipLines();
            getPrimaryFile().setAttribute(ATT_SKIP_LINES, i);
            firePropertyChange(PROP_SKIP_LINES, old, i);
        } catch (IOException ex) {
            LOGGER.log(Level.WARNING, "could not save skip lines property for csv data object {0}", getName());
        }
    }

    @MultiViewElement.Registration(
        displayName = "#LBL_Csv_EDITOR",
    iconBase = ICON_BASE,
    mimeType = MIME_TYPE,
    persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED,
    preferredID = "Csv",
    position = 2000)
    @Messages("LBL_Csv_EDITOR=Source")
    public static MultiViewEditorElement createEditor(Lookup lkp) {
        final MultiViewEditorElement mvee = new MultiViewEditorElement(lkp);
        // make source view read only:
        SwingUtilities.invokeLater(new Runnable() {
            @Override public void run() { 
                JEditorPane pane = mvee.getEditorPane();
                pane.setEditable(false);
                // FIXME: skipping BOM workaround for now, as it has some side effects:
                // It opens the source tab at the bottom of the document, and marks it as savable.
                // The extra character in a read only view is less harmful.
//                pane.setText(stripBOM(pane.getText()));
            }
        });
        return mvee;
    }

    private String getHeaderLine() {
        // can't use first value from getPrimaryFile().asLines().iterator():
        // FileObjectLineIterator keeps a Reader open unless iterated all the way...
        
        String s = null;

        try (InputStream is = getPrimaryFile().getInputStream();
                Reader r = new InputStreamReader(is, charset);
                BufferedReader br = new BufferedReader(r)) {
            s = stripBOM(br.readLine());
        } catch (IOException ex) {
            LOGGER.warning(ex.toString());
//            Exceptions.printStackTrace(ex);
        }    
                
        return s;
    }
    
    private String getContents() {
        try {
            return stripBOM(getPrimaryFile().asText(charset.name()));
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
            return null;
        }
    }
    
    // Remove byte order mark when present
    // https://netbeans.org/bugzilla/show_bug.cgi?id=161015
    private static String stripBOM(String s) {
        if (s == null) return null;
        return s.startsWith("\uFEFF") ? s.substring(1) : s;
    }
    
    private class CsvMappableSource implements MappableSource {

        @Override
        public String[] getColumnNames() {
            String header = getHeaderLine();
            if (header != null) {
                CSVReader csvReader = new CSVReader(new StringReader(header), getSeparatorChar());
                try {
                    return csvReader.readNext();
                } catch (IOException ex) {
                    LOGGER.log(Level.WARNING, "could not read CSV column names: {0}", ex.toString());
                }
            }
            return new String[0];
        }

        @Override
        public String getName() {
            return getPrimaryFile().getNameExt();
        }

        @Override
        public List getAttributeDescriptors() {
            List descriptors = new ArrayList<>();
            for (String name : getColumnNames()) {
                descriptors.add(new SourceAttributeDescriptor(name, String.class));
            }
            return descriptors;
        }

        @Override
        public String[][] getContents() {

            try {
                String contents = CsvDataObject.this.getContents();
                int skipLines = getSkipLines();
                CSVReader csvReader = new CSVReader(new StringReader(contents), getSeparatorChar(), DEFAULT_QUOTE_CHAR, skipLines);
                List rows = csvReader.readAll();
                return rows.toArray(new String[0][0]);
            } catch (IOException ex) {
                LOGGER.warning(ex.toString());
            }

            return new String[0][0];
        }

        @Override
        public SourceRecordIterator getRecordIterator() {
            return new CsvRecordGenerator();
        }

        @Override
        public SourceRecord getGlobals() {
            return SourceRecord.empty();
        }
    }

    private class CsvRecordGenerator extends SourceRecordGenerator {
        private final List descriptors = mappableSource.getAttributeDescriptors();
        private final CSVReader csvReader;

        public CsvRecordGenerator() {
            String contents = CsvDataObject.this.getContents();
            int skipLines = getSkipLines();
            csvReader = new CSVReader(new StringReader(contents), getSeparatorChar(), DEFAULT_QUOTE_CHAR, skipLines);
        }

        @Override
        protected SourceRecord getNextRecord() {
            if (csvReader == null) { return null; }
            
            try {
                String[] next = csvReader.readNext();
                return next == null ? null : SourceRecord.createFromColumnValues(descriptors, next);
            } catch (IOException ex) {
                LOGGER.warning(ex.toString());
                return null;
            }
        }

        @Override
        public void close() throws IOException {
            if (csvReader != null) {
                csvReader.close();
            }
        }
        
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy