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

de.tsl2.nano.bean.BeanFileUtil Maven / Gradle / Ivy

Go to download

TSL2 Framework Descriptor (currency-handling, generic formatter, descriptors for beans, collections, actions and values)

The newest version!
package de.tsl2.nano.bean;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.text.Format;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;

import de.tsl2.nano.bean.def.Bean;
import de.tsl2.nano.bean.def.BeanCollector;
import de.tsl2.nano.bean.def.IAttributeDefinition;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.cls.BeanAttribute;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.util.FileUtil;
import de.tsl2.nano.core.util.FormatUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;

/**
 * Provides read/write files to/from flat files like csv, tabsheet, markdown-table and html-table
 * 
 * @author Thomas Schneider
 */
public class BeanFileUtil {
    private static final Log LOG = LogFactory.getLog(BeanFileUtil.class);

    public enum FileType {CSV, TABSHEET, HTML, MARKDOWN_TABLE};
    
    /**
     * convenience for {@link #fromFlatFile(Reader, Class, String...)}.
     */
    public static  Collection fromFile(String fileName, final FileType fileType, Class rootType, String... attributeNames) {
    	assert fileType != null && !Util.in(fileType, FileType.HTML) : "FileType HTML is not supported!";
    	
        return Util.trY(() -> fromFlatFile(reader(fileName),
                FileType.CSV.equals(fileType) ? "," : "\t",
                rootType,
                null,
                attributeNames));
    }

    /**
     * convenience for {@link #fromFlatFile(Reader, Class, String...)}.
     */
    public static  Collection fromFlatFile(String fileName, Class rootType, String... attributeNames) {
        return Util.trY(() -> fromFlatFile(reader(fileName), rootType, null, attributeNames));
    }

    /**
     * convenience for {@link #fromFlatFile(Reader, Class, String...)}.
     */
    public static  Collection fromFlatFile(String fileName,
            String separation,
            Class rootType,
            String... attributeNames) {
        return Util.trY(() ->fromFlatFile(reader(fileName),
                separation,
                rootType,
                null,
                attributeNames));
    }

	private static BufferedReader reader(String fileName) throws FileNotFoundException {
		return new BufferedReader(new FileReader(FileUtil.userDirFile(fileName)));
	}

    /**
     * reads a flat (like csv) file and tries to put the values to the given bean type. the given bean type must have a
     * default constructor.
     * 
     * @param 
     * @param r normally a buffered reader.
     * @param rootType root bean type to be instantiated and filled to the result collection
     * @param attributeNames (optional) simple attribute names or point-separated relation expressions. use null to
     *            ignore the token
     * @return filled collection of beans
     */
    public static  Collection fromFlatFile(Reader r,
            Class rootType,
            Map formats,
            String... attributeNames) {
        return fromFlatFile(r, null, Bean.newBean(rootType), formats, attributeNames);
    }

    /**
     * reads a flat (like csv) file and tries to put the values to the given bean type. the given bean type must have a
     * default constructor.
     * 
     * @param 
     * @param r normally a buffered reader.
     * @param separation separation character. if this is null, the attributeNames must contain at least one
     *            column-index.
     * @param rootType root bean type to be instantiated and filled to the result collection
     * @param attributeNames (optional) simple attribute names or point-separated relation expressions. use null to
     *            ignore the token
     * @return filled collection of beans
     */
    public static  Collection fromFlatFile(Reader r,
            String separation,
            Class rootType,
            Map formats,
            String... attributeNames) {
        return fromFlatFile(r, separation, Bean.newBean(rootType), formats, attributeNames);
    }

    /**
     * reads a flat file (like csv) and tries to put the values to the given bean type. the given bean type should have
     * a default constructor. the given bean holds an example instance of your root-type. it is possible to provide an
     * overridden bean, to implement the method {@link Bean#newInstance(Object...)} to initialize your desired instance.
     * a new instance will be created on each new line.
     * 

* there are two possibilities to use this method:
* - with a field separator (like comma or semicolon)
* - with line-column definitions (like '1-10:myBeanAttributeName) *

* * with the first alternative, you give a separator (not null) and the pure attribute names of the given rootType * (included in your bean). it is possible to give attribute-relations like 'myAttr1.myAttr2.myAttr3'. to ignore * fields, use null as beanattribute-name. *

* * the second alternative needs all beanattribute names with a column-prefix like 'begin-end:attributename'. for * example: 1-11:date. it is possible to use bean relations as in the first alternative, too. *

* Please notice, that the column-indexes are one-based - the first column is 1 - and the end-index will not be * included like in String.substring(begin, end). e.g. to read '01.01.2001' you need 1-11. the indexes are * equivalent to standard texteditors like notepad++. * * @param root type * @param r normally a buffered reader. * @param separation separation character. if this is null, the attributeNames must contain at least one * column-index. * @param bean root bean type to be instantiated and filled to the result collection * @param attributeFormats (optional) some format-instances to parse to the right object. used if found, otherwise * standard formatters will be used. * @param attributeNames simple attribute names or point-separated relation expressions. use null to ignore the * token * @return filled collection of beans */ public static Collection fromFlatFile(Reader r, String separation, Bean bean, Map attributeFormats, String... attributeNames) { /* * do some validation checks */ if (Util.isEmpty(attributeNames)) { attributeNames = bean.getAttributeNames(); } if (separation == null) { boolean hasColumnIndexes = false; for (String n : attributeNames) { if (n != null && n.contains(":")) { hasColumnIndexes = true; break; } } if (!hasColumnIndexes) { throw ManagedException .implementationError( "if you don't give a separation-character, you should give at least one column-index in your attribute-names", null); } } final Collection result = new LinkedList(); final StreamTokenizer st = new StreamTokenizer(r); /* * to remove parsing of numbers by the tokenizer we have to reset all! * ugly jdk implementation - perhaps we should use Scanner. */ st.resetSyntax(); st.wordChars(0x00, 0xFF); // st.quoteChar('\"'); st.whitespaceChars('\r', '\r'); st.whitespaceChars('\n', '\n'); st.eolIsSignificant(true); st.commentChar('#'); // st.slashSlashComments(true); // st.slashStarComments(true); int ttype = 0; final Class rootType = bean.getClazz(); // bean.newInstance(); final Map errors = new Hashtable(); final String rootInfo = rootType.getSimpleName() + "."; /* * prepare the format cache to parse strings with performance */ final Map formatCache = new HashMap(); if (attributeFormats != null) { formatCache.putAll(attributeFormats); } /* * prepared fixed columns */ int begin, end; String attrName; final Map attributeColumns = new LinkedHashMap(attributeNames.length); for (int i = 0; i < attributeNames.length; i++) { if (separation != null || attributeNames[i] == null) { attributeColumns.put((attributeNames[i] != null ? attributeNames[i] : "null:" + String.valueOf(i)), null); } else { begin = Integer.valueOf(StringUtil.substring(attributeNames[i], null, "-")); end = Integer.valueOf(StringUtil.substring(attributeNames[i], "-", ":")); if (end <= begin || begin < 0 || end < 1) { throw new IllegalArgumentException("The given range " + attributeNames[i] + " is illegal!"); } attrName = StringUtil.substring(attributeNames[i], ":", null); //store one-based indexes attributeColumns.put(attrName, new Point(begin - 1, end - 1)); } } final Set cols = attributeColumns.keySet(); boolean filled = false; /* * do the reading, collecting all errors to throw only one exception at the end */ try { int markdownTableHeader = 0; boolean isMarkdownTable = false; String t; while ((ttype = st.nextToken()) != StreamTokenizer.TT_EOF) { if (ttype != StreamTokenizer.TT_EOL && st.sval.trim().length() > 0) { // ignore markdown table styling if (markdownTableHeader < 3 && (st.sval.startsWith("|") || st.sval.startsWith("--"))) { markdownTableHeader++; if (markdownTableHeader == 3) isMarkdownTable = true; continue; } else if (isMarkdownTable) { st.sval = st.sval.substring(1).replaceAll("\\s*[|]\\s*", separation); } bean.newInstance(); int lastSep = 0; for (final String attr : cols) { final Point c = attributeColumns.get(attr); if (c != null) { if (c.x >= st.sval.length() || c.y > st.sval.length()) { throw new StringIndexOutOfBoundsException("The range " + c.x + "-" + c.y + " is not available on line " + st.lineno() + " with length " + st.sval.length() + ":" + st.sval); } t = st.sval.substring(c.x, c.y); } else { t = StringUtil.substring(st.sval, null, separation, lastSep); } lastSep += t.length() + (c != null ? 0 : separation.length()); //at line end, no separation char will occur if (st.sval.length() < lastSep) { lastSep = st.sval.length(); } if (attr == null || attr.startsWith("null:")) { LOG.info("ignoring line " + st.lineno() + ", token '" + t + "' at column " + (lastSep - t.length())); continue; } t = StringUtil.trim(t, " \""); final String info = "reading line " + st.lineno() + ":'" + t + "' -> " + rootInfo + attr; try { Object newValue = null; if (!Util.isEmpty(t)) { final BeanAttribute beanAttribute = BeanAttribute.getBeanAttribute(rootType, attr); Format parser = formatCache.get(attr); if (parser == null) { parser = FormatUtil.getDefaultFormat(beanAttribute.getType(), true); formatCache.put(attr, parser); } newValue = parser.parseObject(t); bean.setValue(beanAttribute.getName(), newValue); } LOG.debug(info + "(" + newValue + ")"); filled = true; } catch (final Exception e) { LOG.info("problem on " + info); LOG.error(e.toString()); errors.put(info, e); } } if (filled) { result.add(bean.getInstance()); } } } } catch (final Exception e) { ManagedException.forward(e); } if (errors.size() > 0) { throw new ManagedException(StringUtil.toFormattedString(errors, 80, true)); } LOG.info("import finished - imported items: " + result.size() + " of type " + rootType.getSimpleName()); return result; } public static void toFile(Collection col, String filename, FileType type) { BeanCollector collector = BeanCollector.getBeanCollector(col, 0); String data; switch (type) { case CSV: data = presentAsCSV(collector); break; case TABSHEET: data = presentAsTabSheet(collector); break; case MARKDOWN_TABLE: data = presentAsMarkdownTable(collector); break; case HTML: data = presentAsHtmlTable(collector); break; default: throw new IllegalArgumentException(Util.asString(type)); } FileUtil.writeBytes(data.getBytes(), filename, false); } /** * delegates to {@link #present(BeanCollector, String, String, String, String, String, String, String, String)}. */ public static String presentAsCSV(BeanCollector collector) { return present(collector, "", "", "", "\n", "", ",", null, null); } /** * delegates to {@link #present(BeanCollector, String, String, String, String, String, String, String, String)}. */ public static String presentAsTabSheet(BeanCollector collector) { return present(collector, "", "", "", "\n", "", "\t", null, null); } /** * creates a simple html-table as presentation for the given collector. * * @param collector * @return see {@link #present(BeanCollector, String, String, String, String, String, String, String, String)} */ public static String presentAsHtmlTable(BeanCollector collector) { return present(collector, "

\n", "
", "", "\n", "", "\"", "", ":
\""); } public static String presentAsMarkdownTable(BeanCollector collector) { return present(collector, StringUtil.fixString(79, '-'), "\n" + StringUtil.fixString(79, '-'), "\n|", "", " ", " |", null, null); } /** * creates a string representing all items with all attributes of the given beancollector (holding a collection of * items). *

* All parameters without nameBegin and nameEnd must not be null! * * @param collector holding a list - defining the attribute presentation. * @param header text header * @param footer text footer * @param rowBegin text on a new line * @param rowEnd text on line end * @param colBegin text on new column * @param colEnd text on column end * @param nameBegin (optional) if not null, starting text of a fields name. if null, no field name will be presented * @param nameEnd (optional) if not null, ending text of a fields name. if null, no field name will be presented * @return string presentation of given collector */ public static String present(BeanCollector collector, String header, String footer, String rowBegin, String rowEnd, String colBegin, String colEnd, String nameBegin, String nameEnd) { Collection c = collector.getCurrentData(); List attributes = collector.getBeanAttributes(); StringBuilder buf = new StringBuilder(c.size() * attributes.size() * 30 + 100); buf.append(header); if (!Util.isEmpty(header) && ENV.get("bean.flatfile.createheader", true)) { buf.append(rowBegin); for (IAttributeDefinition a : attributes) { buf.append(colBegin + (nameBegin != null && nameEnd != null ? nameBegin + a.getName() + nameEnd : "") + a.getName() + colEnd); } buf.append(rowEnd + header); } for (Object o : c) { buf.append(rowBegin); for (IAttributeDefinition a : attributes) { buf.append(colBegin + (nameBegin != null && nameEnd != null ? nameBegin + a.getName() + nameEnd : "") + collector.getColumnText(o, a) + colEnd); } buf.append(rowEnd); } buf.append(footer); return buf.toString(); } } /** * To avoid using package awt, we can't use java.awt.Point - but we need a simple Point. */ class Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy