SqlTemplateValueTypeRegistry.java
package com.github.mygreen.splate.type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.lang.Nullable;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
/**
* {@link SqlTemplateValueType}の管理クラス。
*
*
* @author T.TSUCHIE
*
*/
public class SqlTemplateValueTypeRegistry {
/**
* クラスタイプで関連付けられた{@link SqlTemplateValueType}のマップ
*/
private Map<Class<?>, SqlTemplateValueType<?>> typeMap = new ConcurrentHashMap<>();
/**
* パスで関連づけられた{@link SqlTemplateValueType}のマップ
*/
private Map<String, ValueTypeHolder> pathMap = new ConcurrentHashMap<>();
public SqlTemplateValueTypeRegistry() {
}
/**
* {@code SqlTemplateValueTypeRegistry}を元にコンストラクタを作成します。
*
* @param registry 作成元となる {@link SqlTemplateValueType}の管理情報。
*/
public SqlTemplateValueTypeRegistry(final @NonNull SqlTemplateValueTypeRegistry registry) {
this.typeMap.putAll(registry.typeMap);
this.pathMap.putAll(registry.pathMap);
}
/**
* プロパティパスに対応した値の変換処理を取得します。
* @param requiredType プロパティのクラスタイプ。
* @param propertyPath プロパティのパス。
* @return 対応する変換処理の実装を返します。見つからない場合は {@literal null} を返します。
*/
@Nullable
public SqlTemplateValueType<?> findValueType(@Nullable Class<?> requiredType, @Nullable String propertyPath) {
if(propertyPath != null) {
// 完全なパスで比較
SqlTemplateValueType<?> valueType = getValueTypeByPropertyPath(propertyPath, requiredType);
if(valueType != null) {
return valueType;
}
// インデックスを除去した形式で比較
final List<String> strippedPaths = new ArrayList<>();
addStrippedPropertyPaths(strippedPaths, "", propertyPath);
for(String strippedPath : strippedPaths) {
valueType = getValueTypeByPropertyPath(strippedPath, requiredType);
if(valueType != null) {
return valueType;
}
}
}
if(requiredType != null) {
// 見つからない場合は、クラスタイプで比較
if(typeMap.containsKey(requiredType)) {
return typeMap.get(requiredType);
}
// 列挙型の場合
if(requiredType.isEnum()) {
return typeMap.get(Enum.class);
}
}
return null;
}
/**
* 指定したプロパティパスに完全一致する変換処理を取得します。
*
* @param propertyPath プロパティパス
* @param requiredType プロパティのクラスタイプ
* @return 対応する対応する変換処理。見つからない場合は、{@literal null} を返します。
*/
@Nullable
private SqlTemplateValueType<?> getValueTypeByPropertyPath(final String propertyPath, final Class<?> requiredType) {
ValueTypeHolder holder = pathMap.get(propertyPath);
return (holder != null ? holder.get(requiredType) : null);
}
/**
* {@link SqlTemplateValueType} を登録します。
* @param <T> 関連付ける型
* @param type 関連付けるクラスタイプ
* @param valueType {@link SqlTemplateValueType}の実装
*/
public <T> void register(@NonNull Class<T> type, @NonNull SqlTemplateValueType<T> valueType) {
this.typeMap.put(type, valueType);
}
/**
* プロパティのパスを指定して{@link SqlTemplateValueType} を登録します。
* <p>SQLテンプレート中の変数(プロパティパス/式)を元に関連付ける再に使用します。
*
* @param <T> 関連付ける型
* @param propertyPath プロパティパス/式
* @param type 関連付けるクラスタイプ
* @param valueType {@link SqlTemplateValueType}の実装
*/
public <T> void register(@NonNull String propertyPath, @NonNull Class<T> type, @NonNull SqlTemplateValueType<T> valueType) {
this.pathMap.put(propertyPath, new ValueTypeHolder(type, valueType));
}
/**
* パスからリストのインデックス([1])やマップのキー([key])を除去したものを構成する。
* <p>SpringFrameworkの「PropertyEditorRegistrySupport#addStrippedPropertyPaths(...)」の処理</p>
* @param strippedPaths 除去したパス
* @param nestedPath 現在のネストしたパス
* @param propertyPath 処理対象のパス
*/
protected void addStrippedPropertyPaths(List<String> strippedPaths, String nestedPath, String propertyPath) {
final int startIndex = propertyPath.indexOf('[');
if (startIndex != -1) {
final int endIndex = propertyPath.indexOf(']');
if (endIndex != -1) {
final String prefix = propertyPath.substring(0, startIndex);
final String key = propertyPath.substring(startIndex, endIndex + 1);
final String suffix = propertyPath.substring(endIndex + 1, propertyPath.length());
// Strip the first key.
strippedPaths.add(nestedPath + prefix + suffix);
// Search for further keys to strip, with the first key stripped.
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix, suffix);
// Search for further keys to strip, with the first key not stripped.
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix + key, suffix);
}
}
}
@RequiredArgsConstructor
private static class ValueTypeHolder {
private final Class<?> registeredType;
private final SqlTemplateValueType<?> valueType;
SqlTemplateValueType<?> get(final Class<?> requiredType) {
if(registeredType.isAssignableFrom(requiredType)) {
return valueType;
}
return null;
}
}
}