com.iofairy.si.SI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functional Show documentation
Show all versions of functional Show documentation
Functional Programming for Java 8+ and compatible with the modular system of Java 9+.
/*
* Copyright (C) 2021 iofairy,
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.iofairy.si;
import com.iofairy.except.CircularReferencesException;
import com.iofairy.except.UndefinedVariableException;
import com.iofairy.except.UnexpectedParameterException;
import com.iofairy.except.UnexpectedTypeException;
import com.iofairy.top.G;
import com.iofairy.top.O;
import com.iofairy.top.S;
import com.iofairy.tuple.Tuple;
import java.util.*;
import static com.iofairy.si.SIBase.*;
/**
* String Interpolator. It's not thread-safe.
* 字符串插值器(非线程安全)
*
* @since 0.0.1
*/
public class SI {
private final static int CACHE_SIZE = 1000;
private final static int NESTED_CACHE_SIZE = 500;
private final static int KEY_CACHE_SIZE = 2000;
private final static Map> TEMPLATE_CACHE = Collections.synchronizedMap(new LRUCache<>(CACHE_SIZE));
private final static Map> NESTED_TEMPLATE_CACHE = Collections.synchronizedMap(new LRUCache<>(NESTED_CACHE_SIZE));
private final static Map KEY_CACHE = Collections.synchronizedMap(new LRUCache<>(KEY_CACHE_SIZE));
private final Map valueMap = new HashMap<>(); // 读多写少,未加同步机制
/**
* 是否开启嵌套插值
*/
private boolean enableSIInVariables = false;
/**
* 是否在 {@link #valueMap} 的值中开启嵌套插值({@link #enableSIInVariables} 为 {@code true} 时才有效)
*/
private boolean enableSIInValues = false;
/**
* 是否抛出异常,当 {@link #valueMap} 中不存在指定的变量
*/
private boolean enableUndefinedVariableException = false;
private final static String MSG_UNEXPECTED_PARAM = "This parameter is a key, the key must be end with \" ->\" or \" >>>\" or \" >>\". ";
public SI() {
}
public SI(final Tuple... tuples) {
tuplesPutToMap(tuples);
}
public SI(final Map valueMap) {
if (valueMap != null) this.valueMap.putAll(valueMap);
}
public static SI of(final Tuple... tuples) {
return new SI(tuples);
}
public static SI of(final Map map) {
return new SI(map);
}
/**
* Instantiate an SI object by key-value pairs.
*
* @param kvs key-value pairs
* @return SI object
* @throws RuntimeException if the kvs length not be even.
* @throws NullPointerException if the key is null.
* @throws UnexpectedTypeException if the key is not String.
* @throws UnexpectedParameterException if the key is not end with " ->" or " >>>" or " >>".
* @since 0.0.1
*/
public static SI of(Object... kvs) {
Map kvMap = toMap(false, false, kvs);
return of(kvMap);
}
/**
* Instantiate an SI object by key-value pairs, and key must be end with " ->" or " >>>" or " >>",
* and key will be removed leading and trailing whitespace.
* Examples:
* {@code
* String infoTemplate = "ip: ${ip}---port: ${port}---db: ${db}---otherInfo: ${other_info}";
*
* SI si = SI.init(" ip ->", "127.0.0.1",
* " db ->", "testdb",
* " port ->", 3306,
* " dbType ->", "mysql",
* " other_info ->", Tuple.of("isCluster", true),
* "description ->", new Object());
*
* String dbInfo = si.$(infoTemplate);
* }
*
* @param kvs key-value pairs
* @return SI object
* @throws RuntimeException if the kvs length not be even.
* @throws NullPointerException if the key is null.
* @throws UnexpectedTypeException if the key is not String.
* @throws UnexpectedParameterException if the key is not end with " ->" or " >>>" or " >>".
* @since 0.0.1
*/
public static SI init(Object... kvs) {
Map kvMap = toMap(true, true, kvs);
return of(kvMap);
}
/**
* Instantiate an SI object by key-value pairs, and key must be end with " ->" or " >>>" or " >>".
* Examples:
* {@code
* SI si = SI.load("ip ->", "127.0.0.1",
* "port ->", 3306,
* "db ->", "testdb",
* "dbType ->", "mysql",
* "other_info ->", Tuple.of("isCluster", true),
* "description ->", new Object());
*
* String dbInfo = si.$("ip: ${ip}---port: ${port}---db: ${db}---otherInfo: ${other_info}");
* }
*
* @param kvs key-value pairs
* @return SI object
* @throws RuntimeException if the kvs length not be even.
* @throws NullPointerException if the key is null.
* @throws UnexpectedTypeException if the key is not String.
* @throws UnexpectedParameterException if the key is not end with " ->" or " >>>" or " >>".
* @since 0.0.1
*/
public static SI load(Object... kvs) {
Map kvMap = toMap(true, false, kvs);
return of(kvMap);
}
public SI add(Tuple... tuples) {
tuplesPutToMap(tuples);
return this;
}
public SI add(Map valueMap) {
if (valueMap != null) this.valueMap.putAll(valueMap);
return this;
}
/**
* Add key-value pairs to this SI object.
*
* @param kvs key-value pairs
* @return this SI object
* @throws RuntimeException if the kvs length not be even.
* @throws NullPointerException if the key is null.
* @throws UnexpectedTypeException if the key is not String.
* @since 0.0.1
*/
public SI add(Object... kvs) {
Map kvMap = toMap(false, false, kvs);
return this.add(kvMap);
}
/**
* Fill key-value pairs to this SI object. And key must be end with " ->" or " >>>" or " >>",
* and key will be removed leading and trailing whitespace.
* Examples:
* {@code
* String infoTemplate = "ip: ${ip}---port: ${port}---db: ${db}---otherInfo: ${other_info}";
* SI si = SI.of();
* si.fill(" ip ->", "127.0.0.1",
* " db ->", "testdb",
* " port ->", 3306,
* " dbType ->", "mysql",
* " other_info ->", Tuple.of("isCluster", true),
* "description ->", new Object());
*
* String dbInfo = si.$(infoTemplate);
* }
*
* @param kvs key-value pairs
* @return this SI object
* @throws RuntimeException if the kvs length not be even.
* @throws NullPointerException if the key is null.
* @throws UnexpectedTypeException if the key is not String.
* @throws UnexpectedParameterException if the key is not end with " ->" or " >>>" or " >>".
* @since 0.0.1
*/
public SI fill(Object... kvs) {
Map kvMap = toMap(true, true, kvs);
return this.add(kvMap);
}
public SI set(Tuple... tuples) {
valueMap.clear();
return this.add(tuples);
}
public SI set(Map valueMap) {
this.valueMap.clear();
return this.add(valueMap);
}
/**
* Reset this SI object with key-value pairs.
*
* @param kvs key-value pairs
* @return this SI object
* @throws RuntimeException if the kvs length not be even.
* @throws NullPointerException if the key is null.
* @throws UnexpectedTypeException if the key is not String.
* @since 0.0.1
*/
public SI set(Object... kvs) {
valueMap.clear();
Map kvMap = toMap(false, false, kvs);
return this.add(kvMap);
}
public SI del(String... keys) {
if (keys != null) {
Arrays.stream(keys).forEach(valueMap::remove);
}
return this;
}
private void tuplesPutToMap(Tuple... tuples) {
if (tuples != null) {
Arrays.stream(tuples)
.filter(e -> e != null && e.arity() != 0)
.forEach(t -> valueMap.putAll(t.toMap()));
}
}
/**
* Interpolating for strings.
* 执行插值程序,解析字符串
*
* @param source source string
* @return string that has been processed
* @throws CircularReferencesException when the circular reference occurs
* @throws UndefinedVariableException No variable was found during string interpolation when {@link #enableUndefinedVariableException} is {@code true}.
* @since 0.0.1
*/
public String $(CharSequence source) {
if (source == null) return null;
if (S.isBlank(source)) return source.toString();
String sourceString = source.toString();
StringBuilder interpolated = new StringBuilder();
if (enableSIInVariables) {
List