SourceTree における変更の破棄

Windows環境では SourceTreeをアップデートしてもツールバーの破棄ボタン、

あるいは、変更ソースを右クリックして表示されるコンテキストメニュー
破棄は消えていないが、

Mac環境にインストールした SoureceTree ではこの「破棄」が存在しない。
GITコマンドで破棄の操作、git checkout の操作をした方が良いかも。
戻したい対象を指定する場合

git checkout {対象ソースのPATH}

全て戻したい場合

git checkout .

同様に戸惑った人はいるみたいで、下記にありました。隠れてるんですね。
https://qiita.com/Kobutorina_hato/items/9732db0e96fe0685d48f

TSVを総称型指定のクラスで読込み

昔作成して公開している CSVの読み書き、
GitHub - yipuran/yipuran-csv: Java CSV read and write
の中の
csvobject · yipuran/yipuran-csv Wiki · GitHub
から、CsvObject を継承して、TSV を総称型指定のクラスで読込む。
変更点は、

  • org.yipuran.csv4j.CSVReader からデリミタ指定をカンマ区切りからタブ文字に変えるだけ
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Stream;
import org.yipuran.csv.BOMfunction;
import org.yipuran.csv.CsvObject;
import org.yipuran.csv4j.CSVReader;
import org.yipuran.csv4j.ProcessingException;
/**
 * TSV を総称型指定のクラスで読込み
 */
public class TsvObject<T> extends CsvObject<T>{
    private static final char DELIMITER = '\t';
    private boolean blankIsNull = false;
    private Class<T> cls;
    private List<Class<?>> typelist;
    private List<Method> methodlist;
    private DateTimeFormatter dateFormatter =  DateTimeFormatter.ISO_LOCAL_DATE;//  DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private DateTimeFormatter localdatetimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    private DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_LOCAL_TIME;
    private Function<String, Boolean> booleanReader = s -> Boolean.parseBoolean(s.toLowerCase());

    /**
     * コンストラクタ.
     * @param t 可変長引数、指定しても使用はされない
     */
    @SuppressWarnings("unchecked")
    public TsvObject(T...t) {
        cls = (Class<T>)t.getClass().getComponentType();
    }
    /**
     * ブランク→null指定.
     * @param true=ブランク、",," は、null として読み込む。
     */
    public void setBlanknull(boolean blankIsNull) {
        this.blankIsNull = blankIsNull;
    }

    /**
     * 総称型指定TSV読込み実行.
     * @param inReader InputStreamReader
     * @param biconsumer コンテンツ行BiConsumer、TSV行読込みカウント(1始まり)と総称型Tのオブジェクト
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public void read(InputStreamReader inReader, BiConsumer<Integer, T> biconsumer) throws IOException, ProcessingException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER, getComment(),
                                         blankIsNull);
        try{
            typelist = new ArrayList<>();
            methodlist = new ArrayList<>();
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (isHasHeader() && lineCount==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                        for(String f:fields){
                            try{
                                Class<?> c =  cls.getDeclaredField(f).getType();
                                Method m = cls.getDeclaredMethod("set" + f.substring(0, 1).toUpperCase() + f.substring(1), c);
                                typelist.add(c);
                                methodlist.add(m);
                            }catch(NoSuchFieldException | NoSuchMethodException e){
                                typelist.add(null);
                                methodlist.add(null);
                            }
                        }
                    }else{
                        T t = cls.getConstructor().newInstance();
                        int i=0;
                        for(String f:fields){
                            Method m = methodlist.get(i);
                            if (m != null){
                                setValue(m, t, i, f);
                            }
                            i++;
                        }
                        biconsumer.accept(lineCount, t);
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
    }
    /**
     * 総称型指定TSV読込み結果Stream生成.
     * @param inReader InputStreamReader
     * @return 総称型 T のStream
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public Stream<T> read(InputStreamReader inReader) throws IOException, ProcessingException{
        Stream.Builder<T> builder = Stream.builder();
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            typelist = new ArrayList<>();
            methodlist = new ArrayList<>();
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (isHasHeader() && lineCount==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                        for(String f:fields){
                            try{
                                Class<?> c =  cls.getDeclaredField(f).getType();
                                Method m = cls.getDeclaredMethod("set" + f.substring(0, 1).toUpperCase() + f.substring(1), c);
                                typelist.add(c);
                                methodlist.add(m);
                            }catch(NoSuchFieldException | NoSuchMethodException e){
                                typelist.add(null);
                                methodlist.add(null);
                            }
                        }
                    }else{
                        T t = cls.getConstructor().newInstance();
                        int i=0;
                        for(String f:fields){
                            Method m = methodlist.get(i);
                            if (m != null){
                                setValue(m, t, i, f);
                            }
                            i++;
                        }
                        builder.add(t);
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
        return builder.build();
    }
    /**
     * 総称型指定TSV読込み実行(コンバーター指定).
     * @param inReader InputStreamReader
     * @param converter TSV1行分の文字列リストから、総称型Tを生成取得するコンバーター
     * @param biconsumer コンテンツ行BiConsumer、TSV行読込みカウント(1始まり)と総称型Tのオブジェクト
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public void read(InputStreamReader inReader, Function<List<String>, T> converter,  BiConsumer<Integer, T> biconsumer)
    throws IOException, ProcessingException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            methodlist = new ArrayList<>();
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (!isHasHeader() || lineCount > 0){
                        biconsumer.accept(lineCount, converter.apply(fields));
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
    }
    /**
     * 総称型指定TSV読込み結果Stream生成(コンバーター指定).
     * @param inReader InputStreamReader
     * @param converter TSV1行分の文字列リストから、総称型Tを生成取得するコンバーター
     * @return 総称型 T のStream
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public Stream<T> read(InputStreamReader inReader, Function<List<String>, T> converter)
    throws IOException, ProcessingException{
        Stream.Builder<T> builder = Stream.builder();
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (!isHasHeader() || lineCount > 0){
                        builder.add(converter.apply(fields));
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
        return builder.build();
    }

    private void setValue(Method m, Object obj, int n, String str) throws NoSuchMethodException, SecurityException
    , IllegalAccessException, IllegalArgumentException {
        try{
            if (typelist.get(n).isPrimitive()) {
                if (blankIsNull && str==null) return;
                Class<?> c = typelist.get(n);
                if (c.equals(int.class)) {
                    m.invoke(obj, Integer.parseInt(str));
                }else if(c.equals(long.class)) {
                    m.invoke(obj, Long.parseLong(str));
                }else if(c.equals(double.class)) {
                    m.invoke(obj, Double.parseDouble(str));
                }else if(c.equals(short.class)) {
                    m.invoke(obj, Short.parseShort(str));
                }else if(c.equals(float.class)) {
                    m.invoke(obj, Float.parseFloat(str));
                }else if(c.equals(boolean.class)) {
                    m.invoke(obj, booleanReader.apply(str));
                }
            }else{
                if (typelist.get(n).equals(String.class)) {
                    m.invoke(obj, str);
                }else if(typelist.get(n).equals(Boolean.class)) {
                    m.invoke(obj, booleanReader.apply(str));
                }else if(typelist.get(n).equals(LocalDate.class)) {
                    m.invoke(obj, LocalDate.parse(str, dateFormatter));
                }else if(typelist.get(n).equals(LocalDateTime.class)) {
                    m.invoke(obj, LocalDateTime.parse(str, localdatetimeFormatter));
                }else if(typelist.get(n).equals(LocalTime.class)) {
                    m.invoke(obj, LocalTime.parse(str, timeFormatter));
                }else{
                    Method getter = typelist.get(n).getDeclaredMethod("valueOf", String.class);
                    m.invoke(obj, getter.invoke(null, str));
                }
            }
        }catch(InvocationTargetException e){
        }
    }
}

(使用例)

TSVの1行を任意クラスFoo として読み込む。ただしヘッダ1行目がフィールド名である。

TsvObject<Foo> co = new TsvObject<>();
co.setBlanknull(true);
co.read(new InputStreamReader(in, StandardCharsets.UTF_8), (i, f)->{
    // i=読み出し行番号 1 始まり、
    // f=Fooインスタンス(1行 → Foo 変換結果)
    System.out.println("["+i+"]# "+f.toString());
});
in.close();
TsvObject<Foo> co = new TsvObject<>();
co.setBlanknull(true);
co.read(new InputStreamReader(in, StandardCharsets.UTF_8)).forEach(f->{
    System.out.println(f.toString());
});
in.close();

TSVの書き込み

読込みと同じ方法で対応できる。
oboe2uran.hatenablog.com

TSVの書き込みの場合の継承元は、yipuran-csv の Wiki - CSV書き込みを参照

  • CsvCreator と同じ方法の interface TsvCreator
  • CsvwriteArrayStream と同じ方法の interface TsvwriteArrayStream
  • CsvwriteListStreamと同じ方法の interface TsvwriteListStream

いずれも、org.yipuran.csv4j.CSVWriter を使う時に、デリミタ措定を
カンマ区切りから、タブ区切りに変更することと、
1行分作成の Stream の join でタブ文字を指定するように
変更することである。

使い方は、yipuran-csv の Wiki - CSV書き込み と同じである。

TsvCreator

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.yipuran.csv.BOMfunction;
import org.yipuran.csv4j.CSVWriter;
/**
 * TSV作成 interface
 */
@FunctionalInterface
public interface TsvCreator extends Serializable{
    /**
     * TSV出力行数分の Colleactionを返す Supplier を取得する.
     * @return Supplier<Collection<String[]>>
     */
    public Supplier<Collection<String[]>> getSupplier();

    /**
     * TSV出力実行.
     * @param out OutputStream
     * @param charName 文字セット名
     */
    default public void create(OutputStream out, String charName){
        try(OutputStreamWriter writer = new OutputStreamWriter(out, charName)){
            CSVWriter csvWriter = new CSVWriter(writer, '\t');
            for(String[] s:getSupplier().get()){
                for(int i=0;i < s.length;i++){
                    s[i] = s[i]==null ? "" : s[i];
                }
                csvWriter.writeLine(s);
            }
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * ダブルクォート括りTSV出力実行.
     * @param out OutputStream
     * @param charName 文字セット名
     */
    default public void createWithDblQuot(OutputStream out, String charName){
        String lineSeparator = System.getProperty("line.separator");
        try(OutputStreamWriter writer = new OutputStreamWriter(out, charName)){
            for(String[] sary:getSupplier().get()){
                for(int i=0;i < sary.length;i++){
                    sary[i] = sary[i]==null ? "" : sary[i];
                }
                writer.write(tsvline(sary));
                writer.write(lineSeparator);
            }
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * BOM付きUTF-8 TSV出力実行
     * @param out OutputStream
     */
    default public void createBomUTF8(OutputStream out){
        try(OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)){
            BOMfunction.push(out);
            CSVWriter csvWriter = new CSVWriter(writer, '\t');
            for(String[] sary:getSupplier().get()){
                for(int i=0;i < sary.length;i++){
                    sary[i] = sary[i]==null ? "" : sary[i];
                }
                csvWriter.writeLine(sary);
            }
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * BOM付きUTF-8ダブルクォート括り TSV出力実行
     * @param out OutputStream
     */
    default public void createBomUTF8WithDblQuot(OutputStream out){
        String lineSeparator = System.getProperty("line.separator");
        try(OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)){
            BOMfunction.push(out);
            for(String[] sary:getSupplier().get()){
                for(int i=0;i < sary.length;i++){
                    sary[i] = sary[i]==null ? "" : sary[i];
                }
                writer.write(tsvline(sary));
                writer.write(lineSeparator);
            }
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * ダブルクォート括り1行作成.
     * @param ary 1行生成する配列
     * @return ダブルクォート括り1行
     */
    default public String tsvline(String[] ary){
        return "\"" + Arrays.stream(ary).map(s->s.replaceAll("\"","\"\""))
                   .collect(Collectors.joining("\"\t\"")) + "\"";
    }
}

TsvwriteArrayStream
String[] の Stream を指定する場合の為に

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.yipuran.csv.BOMfunction;
import org.yipuran.csv4j.CSVWriter;
/**
 * 配列 Stream から生成
 */
@FunctionalInterface
public interface TsvwriteArrayStream{
    /**
     * TSV出力行数分の String配列の Stream を返す Supplier を取得する.
     * @return Supplier<Stream<String[]>>
     */
    Supplier<Stream<String[]>> getSupplier();

    /**
     * TSV出力実行.
     * @param out OutputStream
     * @param charName 文字セット名
     */
    default public void create(OutputStream out, String charName){
        try(OutputStreamWriter writer = new OutputStreamWriter(out, charName)){
            CSVWriter csvWriter = new CSVWriter(writer, '\t');
            getSupplier().get().map(s->{
                for(int i=0;i < s.length;i++){
                    s[i] = s[i]==null ? "" : s[i];
                }
                return s;
            }).forEach(t->{
                try{
                    csvWriter.writeLine(t);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception ex){
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }
    /**
     * ダブルクォート括りTSV出力実行.
     * @param out OutputStream
     * @param charName 文字セット名
     */
    default public void createWithDblQuot(OutputStream out, String charName){
        String lineSeparator = System.getProperty("line.separator");
        try(OutputStreamWriter writer = new OutputStreamWriter(out, charName)){
            getSupplier().get().map(s->{
                for(int i=0;i < s.length;i++){
                    s[i] = s[i]==null ? "" : s[i];
                }
                return s;
            }).forEach(t->{
                try{
                    writer.write(tsvline(t));
                    writer.write(lineSeparator);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * BOM付きUTF-8 TSV出力実行
     * @param out OutputStream
     */
    default public void createBomUTF8(OutputStream out){
        try(OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)){
            BOMfunction.push(out);
            CSVWriter csvWriter = new CSVWriter(writer, '\t');
            getSupplier().get().map(s->{
                for(int i=0;i < s.length;i++){
                    s[i] = s[i]==null ? "" : s[i];
                }
                return s;
            }).forEach(t->{
                try{
                    csvWriter.writeLine(t);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * BOM付きUTF-8ダブルクォート括り TSV出力実行
     * @param out OutputStream
     */
    default public void createBomUTF8WithDblQuot(OutputStream out){
        String lineSeparator = System.getProperty("line.separator");
        try(OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)){
            BOMfunction.push(out);
            getSupplier().get().map(s->{
                for(int i=0;i < s.length;i++){
                    s[i] = s[i]==null ? "" : s[i];
                }
                return s;
            }).forEach(t->{
                try{
                    writer.write(tsvline(t));
                    writer.write(lineSeparator);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * ダブルクォート括り1行作成.
     * @param ary 1行生成する配列
     * @return ダブルクォート括り1行
     */
    default public String tsvline(String[] ary){
        return "\"" + Arrays.stream(ary).map(s->s.replaceAll("\"","\"\""))
                   .collect(Collectors.joining("\"\t\"")) + "\"";
    }
}

TsvwriteListStream
List<String> の Stream から生成

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.yipuran.csv.BOMfunction;
import org.yipuran.csv4j.CSVWriter;

/**
 * List<String> の Stream から生成
 */
@FunctionalInterface
public interface TsvwriteListStream{

    Supplier<Stream<List<String>>> getSupplier();

    /**
     * TSV出力実行.
     * @param out OutputStream
     * @param charName 文字セット名
     */
    default public void create(OutputStream out, String charName){
        try(OutputStreamWriter writer = new OutputStreamWriter(out, charName)){
            CSVWriter csvWriter = new CSVWriter(writer, '\t');
            getSupplier().get().map(list->list.stream().map(s->s==null ? "" : s)
            .collect(Collectors.toList()))
            .forEach(t->{
                try{
                    csvWriter.writeLine(t);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception ex){
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }
    /**
     * ダブルクォート括りTSV出力実行.
     * @param out OutputStream
     * @param charName 文字セット名
     */
    default public void createWithDblQuot(OutputStream out, String charName){
        String lineSeparator = System.getProperty("line.separator");
        try(OutputStreamWriter writer = new OutputStreamWriter(out, charName)){
            getSupplier().get().map(list->list.stream().map(s->s==null ? "" : s)
            .collect(Collectors.toList()))
            .forEach(t->{
                try{
                    writer.write(tsvline(t));
                    writer.write(lineSeparator);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * BOM付きUTF-8 TSV出力実行
     * @param out OutputStream
     */
    default public void createBomUTF8(OutputStream out){
        try(OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)){
            BOMfunction.push(out);
            CSVWriter csvWriter = new CSVWriter(writer, '\t');
            getSupplier().get().map(list->list.stream().map(s->s==null ? "" : s)
            .collect(Collectors.toList()))
            .forEach(t->{
                try{
                    csvWriter.writeLine(t);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * BOM付きUTF-8ダブルクォート括り TSV出力実行
     * @param out OutputStream
     */
    default public void createBomUTF8WithDblQuot(OutputStream out){
        String lineSeparator = System.getProperty("line.separator");
        try(OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)){
            BOMfunction.push(out);
            getSupplier().get().map(list->list.stream().map(s->s==null ? "" : s)
            .collect(Collectors.toList()))
            .forEach(t->{
                try{
                    writer.write(tsvline(t));
                    writer.write(lineSeparator);
                }catch(IOException ex){
                    throw new RuntimeException(ex);
                }
            });
        }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * Collection→ダブルクォート括り1行作成.
     * @param list 1行生成する文字列 List
     * @return ダブルクォート括り1行
     */
    default public String tsvline(List<String> list){
        return "\"" + list.stream().map(s->s.replaceAll("\"","\"\""))
                   .collect(Collectors.joining("\"\t\"")) + "\"";
    }
}

TSVの読込み

昔作成して公開している CSVの読み書き、
 GitHub - yipuran/yipuran-csv: Java CSV read and write
これは、TSV ファイルで使用することを目的にしていなかった。
長いこと開発をしていて、なかなかTSVファイルを対象にすることが少なかった。
この yipuran-csv の中の、Csvprocess と同じ機能で、TSV読込みは、継承をすることで
次のように用意できる。
 Csvprocess の説明は、→ こちらのWiki
継承して、TSV用に用意するポイントは、

  • 内部で使用する org.yipuran.csv4j.CSVReader のインスタンス生成で、デリミタをカンマ区切りからタブ区切りにすること

後は、Csvprocess と同じメソッドの仕様で使える。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.yipuran.csv.BOMfunction;
import org.yipuran.csv.Csvprocess;
import org.yipuran.csv4j.CSVReader;
import org.yipuran.csv4j.ParseException;
import org.yipuran.csv4j.ProcessingException;
/**
 * Tsvprocess : Csvprocessを継承したTSV読込クラス
 */
public class Tsvprocess extends Csvprocess{
    private boolean blankIsNull = false;
    private static final char DELIMITER = '\t';
    public Tsvprocess(){}
    
    public Tsvprocess(boolean blankIsNull) {
        this.blankIsNull = blankIsNull;
    }
    /**
     * ヘッダ有りTSV読込み実行.
     * @param inReader InputStreamReader
     * @param header ヘッダ行 Consumer
     * @param processor コンテンツ行BiConsumer、TSV行読込みカウント(1始まり)とTSV文字列のList
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public void read(InputStreamReader inReader, Consumer<List<String>> header, BiConsumer<Integer, List<String>> processor) 
    throws IOException, ProcessingException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()), 
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (isHasHeader() && lineCount==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                        header.accept(fields);
                    }else{
                        processor.accept(lineCount, fields);
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
    }
    /**
     * ヘッダ無しTSV読込み実行.
     * @param inReader InputStreamReader
     * @param processor BiConsumer 行のindexとTSV文字列のList
     * @throws IOException
     * @throws ProcessingException
     * @throws ParseException
     */
    @Override
    public void readNoheader(InputStreamReader inReader, BiConsumer<Integer, List<String>> processor)
    throws IOException, ProcessingException, ParseException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            int lineIndex = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (lineIndex==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                    }
                    processor.accept(lineIndex, fields);
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineIndex++;
            }
        }finally{
            reader.close();
        }
    }

    /**
     * ヘッダ有りTSV読込み実行(Map形式読込み).
     * <PRE>
     * ヘッダ行列をキーとして読込み結果をMapで実行
     * </PRE>
     * @param inReader InputStreamReader
     * @param processor コンテンツ行BiConsumer、TSV行読込みカウント(1始まり)とヘッダのキーに対するコンテンツ行の値のMap
     * @throws IOException
     * @throws ProcessingException
     */
    @Override
    public void read(InputStreamReader inReader, BiConsumer<Integer, Map<String, String>> processor)
    throws IOException, ProcessingException{
        CSVReader reader = new CSVReader(new BufferedReader(inReader),
                                         Charset.forName(inReader.getEncoding()),
                                         DELIMITER,
                                         getComment(),
                                         blankIsNull);
        try{
            Map<Integer, String> headerMap = new HashMap<>();
            int lineCount = 0;
            while(true){
                try{
                    List<String> fields = reader.readLine();
                    if (fields.size()==0) break;
                    if (isHasHeader() && lineCount==0){
                        String rep = fields.get(0);
                        if (BOMfunction.match(rep)) {
                            fields.remove(0);
                            fields.add(0, BOMfunction.chop(rep));
                        }
                        int i = 0;
                        for(String key:fields){
                            headerMap.put(i, key);
                            i++;
                        }
                    }else{
                        processor.accept(lineCount,
                            Stream.iterate(0, i->i+1).limit(fields.size())
                            .collect(HashMap<String, String>::new, 
                                     (r, t)->r.put(headerMap.get(t), fields.get(t)), (r, t)->{})
                        );
                    }
                }catch(Exception e){
                    throw new ProcessingException(e, reader.getLineNumber());
                }
                lineCount++;
            }
        }finally{
            reader.close();
        }
    }
}

この Tsvprocess を使用するサンプルは、、、
文字コードUTF-8 で1行目はヘッダ行で読込み
↓ サンプル無いの in変数は、InputStream

Tsvprocess process = new Tsvprocess();
process.read(in, StandardCharsets.UTF_8
, h->{
    // h=List<String> ヘッダ行
}, (i, p)->{
    // i=TSV行番号:1始まり、 p=1行の List<String>
});

文字コードUTF-8 でヘッダ無しで読込み
↓ サンプル無いの in変数は、InputStream

Tsvprocess process = new Tsvprocess();
process.readNoheader(in, StandardCharsets.UTF_8, (n, p)->{
    // n=行番号(0始まり、インデックスとしてのカウント) p=1行の List<String>
});

文字コードUTF-8 で1行目はヘッダ行で、マップのキーとして読み込む

Tsvprocess process = new Tsvprocess(true);
process.read(in, StandardCharsets.UTF_8, (n, m)->{
    // n=行番号(1始まり、ヘッダを除くため) m=1行の Map<String, String>
    m.entrySet().stream().forEach(e->{
        // e.getKey()=ヘッダキー e.getValue()=値
    });
});

ATOM後継の Zed editor の Windows版が、なかなか出てこない

ATOM エディタ開発終了になってしまってからかなり時間が経つけど、
後継の Zed editor は、なかなか、Windows版が出てこない。

https://github.com/zed-industries/zed/issues/5394

JsonNode を複製した操作に気をつける。

うっかり shallow copy のまま操作してしまうのを注意したい。
サンプル

{
  "A" : "a",
  "B" : {
    "B01" : "b01-01234",
    "B02" : {
      "detail" : "test_sample",
      "name" : {
        "type" : "merge",
        "len" : 12
      }
    }
  },
  "C" : [ 100, 160, 300 ]
}

この JSONを表すString jsonstr から生成する JsonNode を以下のようにすると
shallow copy になってしまう。

ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonstr);
JsonNode node2 = node;

((ObjectNode)(node2.path("B").path("B02"))).put("detail", "test_sample");
((ObjectNode)(node2.path("B").path("B02"))).replace("name", mapper.createObjectNode()
                                           .put("type", "merge").put("len", 12));
((ArrayNode)(node2.path("C"))).set(1, 160);

node2 を変更しているつもりが、node を変更していることになる。
ディープコピー(deepCopy)を使うようにして元の JsonNode が変更されないようにする

JsonNode node2 = node.deepCopy();

((ObjectNode)(node2.path("B").path("B02"))).put("detail", "test_sample");
((ObjectNode)(node2.path("B").path("B02"))).replace("name", mapper.createObjectNode()
                                           .put("type", "merge").put("len", 12));
((ArrayNode)(node2.path("C"))).set(1, 160);

node2 は、

{
  "A" : "a",
  "B" : {
    "B01" : "b01-01234",
    "B02" : {
      "detail" : "test_sample",
      "name" : {
        "type" : "merge",
        "len" : 12
      }
    }
  },
  "C" : [ 100, 160, 300 ]
}

MySQL のJSON型の path書式

PostgreSQL とは違って、ルートを$文字で指定して、"->" による連結ではなく
JSON列名 -> path
 あるいは、
JSON列名 ->> path
で、文字列とての参照になる。
path 部分が、$から始まってJSONキーを '.' ピリオド区切り文字で指定する。

例) jdataという列データが以下の時、

{
  "limit": "2024-03-01",
  "other": {
    "len": 38,
    "label": "Abc",
    "width": 23,
    "options": [
      "A",
      "B",
      "C"
    ]
  },
  "price": 320
}

other の label を抽出

SELECT jdata->>'$.other.label' 

priceを文字列として抽出

SELECT jdata->>'$.price'     

priceを抽出 → DECIMAL に CASTする

SELECT cast(jdata->'$.price' as DECIMAL) 

limit の日付を DATE 型へ

SELECT cast(jdata->'$.limit' as DATE)  

配列の先頭

SELECT jdata->>'$.other.options[0]'