Javaメモ

備忘用。

#Java

## FTPClientによるFTPファイル送信（org.apache.commons.net.ftp）
<details><summary>ソース</summary><div>

```java:例
// FTP client.
var client = new FTPClient();
try {
    // FTP接続処理.
    client.connect(FTP_HOST);
    client.enterLocalPassiveMode();
    if (!client.login(USER, PASSWORD)) {
        throw new LoginException("wrong credentials.");
    }
    client.setDefaultTimeout(timeout * 1000);
    client.setSoTimeout(sotimeout * 1000);

    // ファイル削除.
    var retDirPath = Path.of(RECV_FTP_DIR, retFileName).toString().replace('\\', '/');
    client.deleteFile(retDirPath);

    // ファイルのFTP転送.
    final boolean existsFile = Arrays.stream(client.listFiles(SEND_FTP_DIR))
            .map(e -> e.getName()).anyMatch(e -> e.equals(fileName));
    if (existsFile) {
        // 前回送信ファイルが存在した場合.
        throw new Exception();
    }

    // ファイル転送.
    var sendFilePath = Path.of(SEND_DIR, fileName1).toString().replace('\\', '/');
    var sendFtpFilePath = Path.of(SEND_FTP_DIR, fileName1).toString().replace('\\', '/');
    try (var is = new FileInputStream(sendFilePath)) {
        client.storeFile(sendFtpFilePath, is);
    }

    // ディレクトリ移動.
    client.changeWorkingDirectory(RECV_FTP_DIR);

    // 受信ファイルの存在チェック.
    final boolean existsFile = Arrays.stream(client.listFiles(RECV_FTP_DIR))
            .map(e -> e.getName()).anyMatch(e -> e.equals(fileName));
    if (existsFile) {
        // ファイル受信.
        try (var os = new BufferedOutputStream(new FileOutputStream(filePath))) {
            if (!client.retrieveFile(ftpFilePath, os)) {
                throw new FileNotFoundException(ftpFilePath);
            }
        }
    }
} finally {
    // FTP接続の切断.
    client.logout();
    client.disconnect();
}
```
</div></details>


## 正規表現判定
```java:例
var m = Pattern.compile("^(?<code>[0-9]{2})").matcher(str);
return m.find() ? m.group("code") : "";
```

## ある値を取得して、その値から検索した名前を設定
```java:例
public <T> void setValue(final T source, final T target, final Function<String, String> func) {
    IntStream.rangeClosed(1, 10).boxed().map(i -> {
        Method getValue = null;
        Method setValue = null;
        try {
            getValue = source.getClass().getMethod("getValue%02d".formatted(i));
            final String value = (String) getValue.invoke(source);
            setValue = target.getClass().getMethod("setValue%02d".formatted(i), String.class);
            setValue.invoke(target, value);
            return Pair.of(i, value);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException
                | SecurityException e) {
            throw new RuntimeException(e);
        }
    }).forEachOrdered(pair -> {
        Method setValueName = null;
        try {
            setValueName = target.getClass()
                    .getMethod("setValueName%02d".formatted(pair.getKey()), String.class);
            setValueName.invoke(target, func.apply(pair.getValue()));
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException
                | SecurityException e) {
            throw new RuntimeException(e);
        }
    });
}
```

## 対象クラスのメンバー変数がvaluesと同じ型の場合、一律設定
```java:例
public void setValue(final Object target, final List<Object> values) {
    try {
        for (Object value : values) {
            for (Field field : target.getClass().getDeclaredFields()) {
                if (field.getType().isAssignableFrom(value.getClass())) {
                    field.setAccessible(true);
                    field.set(target, value);
                }
            }
        }
    } catch (IllegalArgumentException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}
```

## Javaリフレクションによるsetterメソッド呼び出し
```java:例
Method setCode = null;
try {
    setCode = targetObject.getClass().getMethod("setCode", String.class);
    setCode.invoke(targetObject, "1234");
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
        | InvocationTargetException e) {
    throw new RuntimeException(e);
}
```

## 文字列→Date変換
```java:例
final LocalDateTime ldateTime = LocalDateTime.parse(TARGET_DATE, DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
var date = Date.from(ZonedDateTime.of(ldateTime, ZoneId.systemDefault()).toInstant());
```

## CSV読込（jackson-dataformat-csv使用）
```java:例
// ファイル読込
var csvMapper = new CsvMapper();
csvMapper.configure(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS, true);
var csvSchema = csvMapper.schemaFor(CsvEntity.class)
        .withoutHeader().withColumnSeparator(',').withQuoteChar('"')
        .withLineSeparator("\n").withNullValue("\"\"");
 
try (var reader =
        new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
    var entityList = csvMapper.readerFor(CsvEntity.class)
            .with(csvSchema).readValues(reader).readAll().stream()
            .map(CsvEntity.class::cast).toList();
}
```

## CSV出力（jackson-dataformat-csv使用）
```java:例
var schema = csvMapper.schemaFor(Entity.class).withoutHeader().withColumnSeparator(',').withoutQuoteChar()
    .withLineSeparator("\n").withNullValue("");
String writeValue = csvMapper.writer(schema).writeValueAsString(entity).replace(",", "");
try (var stream = new FileOutputStream(fileFullPath, false)) {
    stream .write(writeValue .getBytes("UTF-8"));
}
```

## 所定ディレクトリから、パスリスト作成
```java
final var pathList = new ArrayList<Path>();
try (final Stream<Path> pathStream = Files.walk(Path.of(TARGET_PATH))) {
    pathStream.map(Path::toFile).filter(File::isFile)
    .filter(f -> f.getName().matches(regFileName)).map(File::toPath).forEachOrdered(pathList::add);
}
```

## Eclipse + Java + Maven環境のビルドエラー時対処手順
```
■ビルドエラー時対処手順（上から順に解決まで試す） 
１．Alt + F5 でmaven更新する 
２．すべて（プロジェクト）ビルド 
３．cleanビルド 
４．mavenビルド 
　プロジェクト依存関係順に 
　maven clean -> maven install 
　する。 
５．Eclipse再起動 
６．EclipseのClean起動 
　Eclipseの格納パスにある「eclipse.exe -clean.cmd」を実行 
７．.m2フォルダ消してから４．を実施 
　%USERPROFILE%/.m2/repository 
　ごと削除して、４．を実施 
８．git資産を全削除して、全部cloneしなおしてビルド 
９．Eclipse削除して、再度環境構築
```

## 正規表現
```java:文字列抽出
final String REG_PARSE = "(?<hoge>[A-Za-z])-(?<foo>[0-9]{2})";
var m = Pattern.compile(REG_PARSE).matcher("A-12");
var ret = m.find() ? Pair.of(m.group("hoge"), m.group("foo")) : Pair.of("", "");
```

## StreamAPI
```java:Enumのコード値逆引き
static <E extends Enum<E>> E getByCode(Class<? extends BaseEnum<E>> clazz, String code) {
    return Arrays.stream(clazz.getEnumConstants()).filter(e -> e.getCode().equals(code)).findFirst().orElse(null);
}
```

```java:groupingByの例
// In: List<SalesLine>
// Out: 商品コードをキー、販売数合計を値とするマップ
Map<String, Integer> map =
        list.stream().collect(
                Collectors.groupingBy(SalesLine::getProductCode,
                        Collectors.summingInt(SalesLine::getQuantity)
                ));
```
---

## 全角・半角変換など
```java
Normalizer.normalize(q, Normalizer.Form.NFKC);
```

## NUL文字による文字列化→分割
```java
String messages = errors.getAllErrors().stream().map(e -> e.getDefaultMessage()).collect(Collectors.joining(new String(new char[] {0}) ));
List<String> list = messages.split(new String(new char[] {0}));
```

## BigDecimalの小数点以下桁数丸め(カリー)
```java
Predicate<Object> isNull = Objects::isNull;
IntFunction<Function<RoundingMode, Function<BigDecimal, BigDecimal>>> setScale = i -> r -> n -> ((BigDecimal) isNull.test(n) ? BigDecimal.ZERO : n)).setScale(i, r);
```

## 3桁カンマ付与
```java
Predicate<Object> isNull = Objects::isNull;
Predicate<Object> isNumber = obj -> obj instanceof Number;
final Function<Object, String> addComma = obj -> {
    if (isNull.or(isNumber.negate()).test(obj)) {
        return null;
    }
    if (obj instanceof BigDecimal) {
        return String.format("%,d", ((BigDecimal) obj).longValueExact());
    }
    return String.format("%,d", obj);
};
```

## 数値型のゼロ判定
```java
Predicate<Object> isNull = Objects::isNull;
Predicate<Object> isNumber = obj -> obj instanceof Number;
Predicate<Object> isNumberZero = obj -> {
    if (isNull.or(isNumber.negate()).test(obj)) {
        return false;
    }
    if (obj instanceof BigDecimal) {
        return BigDecimal.ZERO.compareTo((BigDecimal) obj) == 0;
    }
    return Objects.equals((long) obj, 0L);
};
```

## Map内のBigDecimalをサマる
```java
hogeMap.merge("key1", BigDecimal.ONE, (BigDecimal sum, BigDecimal val) -> sum.add(val));
```

## BigDecimalへ変換
```java
private BigDecimal toBigDecimal(final Object n) {
    if (Objects.isNull(n)) {
        return BigDecimal.ZERO;
    }
    if (n instanceof java.lang.String) {
        return new BigDecimal((String) n);
    }
    else if (n instanceof Number) {
        return BigDecimal.valueOf((Long) n);
    }
    return BigDecimal.ZERO;
}
```

## オブジェクト不一致判定用
```java
BiPredicate<Object, Object> isNotEquals = ((BiPredicate<Object, Object>)Objects::equals).negate();
```

## DeepCopy
java.util.ArraysによるDeepCopy

```java
int[] copySrc = new int[5];
Arrays.fill(copySrc, 0);
int[] copyDst = Arrays.copyOf(copySrc, copySrc.length);
```

## オブジェクトのDeepCopy
[org.apache.commons.lang3.SerializationUtils#clone](https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/SerializationUtils.html)によるDeepCopy

```java
public Object deepCopy(Serializable state) {
  return SerializationUtils.clone(state);
}
```

## 優先度付きキュー - PriorityQueue
いまいち有効な使い方がわからん。
[AtCoder Regular Contest 028 - B - 特別賞](https://arc028.contest.atcoder.jp/tasks/arc028_2)を解くのに役立つらしい。


```java
PriorityQueue<Integer> pQueue = new PriorityQueue<Integer>();
pQueue.offer(i);    // 追加
pQueue.peek(i);     // 取得（キューから削除しない）
pQueue.poll(i);     // 取得（キューから削除する）
```
`@see` [ダイクストラ法](https://ja.wikipedia.org/wiki/%E3%83%80%E3%82%A4%E3%82%AF%E3%82%B9%E3%83%88%E3%83%A9%E6%B3%95)

## オブジェクトの中身を表示
```java
// commons-lang
System.out.println(ToStringBuilder.reflectionToString(new Hoge()));
// hoge$Hoge@cafb56[id=1,value=hoge,strValues={aaa,bbb,ccc},items=[ddd, eee, fff]]
```
## オブジェクトにハッシュコードを付与
```java
public class Clazz implements Serializable {
  private static final long serialVersionUID = 1L;
  public int hashCode() {
    return HashCodeBuilder.reflectionHashCode(this);
  }
}
```

## オブジェクトの一致判定を簡単に書けるよ
```java
public class Hoge implements Serializable {
    private static final long serialVersionUID = 1L;
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }
}
```

## 区分値判定
Enum使いなさいよ。ですよね～

```Java
if ( Arrays.asList("Cat", "Dog", "Pig").contains(hoge) ) {
  // Do somthings.
}
```

## Listの初期化
```java
List<String> list = new ArrayList<>(Arrays.asList("Cat", "Dog", "Pig"));
```

## ファイル名の昇順ソート
```java
// ファイル名 昇順にソート
List<File> csvFileList = new ArrayList<File>();
csvFileList.add( new File("###") );
csvFileList.add( new File("BBB") );
csvFileList.add( new File("111") );
csvFileList.add( new File("AAA") );

Collections.sort( csvFileList, new Comparator<File>(){
   @Override
   public int compare( File o1, File o2 ) {
       return StringUtils.compare( o1.getName(), o2.getName() );
   }
});

IterableUtils.forEach( csvFileList, new Closure<File>(){
   @Override
   public void execute( File file ) {
       System.out.println( ReflectionToStringBuilder.toString( file ) );
   }
});
```

# Spring
## リソースファイル取得
```java
@Autowired
ResourceLoader resourceLoader;

final Resource resource = resourceLoader.getResource("classpath:path/to/file");
```

## BindingResultのメッセージをString配列にする
```java
# org.springframework.validation.BindingResult errors;
setMessages(errors.getAllErrors().stream().map(e -> e.getDefaultMessage()).toArray(String[]::new));
```