/**
* jetbrick-template
* http://subchen.github.io/jetbrick-template/
*
* Copyright 2010-2014 Guoqiang Chen. All rights reserved.
* Email: subchen@gmail.com
*
* 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 jetbrick.template.utils;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
@SuppressWarnings("unchecked")
public abstract class ConfigSupport<T extends ConfigSupport<T>> {
private static final Pattern PARAMETER_PATTERN = Pattern.compile("\\$\\{([^}]*)\\}");
public T loadFile(File file) {
try {
return load(new FileInputStream(file), file.getName());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public T loadClasspath(String classpath) {
if (classpath.startsWith("/")) {
classpath = classpath.substring(1);
}
InputStream is = ClassLoaderUtils.getContextClassLoader().getResourceAsStream(classpath);
return load(is, classpath);
}
public T load(String location) {
if (location.startsWith("classpath:")) {
location = location.substring("classpath:".length());
return loadClasspath(location);
} else if (location.startsWith("file:")) {
location = location.substring("file:".length());
return loadFile(new File(location));
} else {
return loadFile(new File(location));
}
}
public T loadSerlvetResource(ServletContext servletContext, String location) {
if (location.startsWith("classpath:")) {
location = location.substring("classpath:".length());
return loadClasspath(location);
} else if (location.startsWith("file:")) {
location = location.substring("file:".length());
return loadFile(new File(location));
} else {
if (!location.startsWith("/")) {
location = "/" + location;
}
InputStream is = servletContext.getResourceAsStream(location);
return load(is, location);
}
}
private T load(InputStream is, String name) {
if (is == null) {
throw new IllegalStateException("InputStream not found: " + name);
}
return load(is);
}
public T load(InputStream is) {
if (is == null) return (T) this;
try {
Properties config = new Properties();
config.load(is);
return load(config);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
IoUtils.closeQuietly(is);
}
}
public T load(Properties config) {
if (config == null) return (T) this;
Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
String name = WordUtils.toPropertyName(field.getName());
String value = config.getProperty(name);
if (value == null) continue;
setFieldValue(field, value);
}
return (T) this;
}
public T load(String name, String value) {
try {
String fieldName = WordUtils.toCamelCase(name);
Field field = getClass().getDeclaredField(fieldName);
setFieldValue(field, value);
} catch (Exception e) {
throw ExceptionUtils.uncheck(e);
}
return (T) this;
}
protected void setFieldValue(Field field, String value) {
if (field == null || Modifier.isFinal(field.getModifiers())) return;
field.setAccessible(true);
Class<?> type = field.getType();
try {
if (List.class.isAssignableFrom(type)) {
List<Object> values = (List<Object>) field.get(this);
if (values == null) {
values = new ArrayList<Object>();
field.set(this, values);
}
type = String.class; // String is the default Class
// try to lookup generic type for List<?>
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
Type[] actualTypeArgs = ((ParameterizedType) genericType).getActualTypeArguments();
if (actualTypeArgs != null && actualTypeArgs.length > 0) {
Type actualType = actualTypeArgs[0];
if ((actualType instanceof Class)) {
type = (Class<?>) actualType;
} else if (actualType instanceof ParameterizedType) {
type = (Class<?>) ((ParameterizedType) actualType).getRawType();
}
}
}
if (value == null) return;
for (String val : split(field, value)) {
val = val.trim();
if (val.length() > 0) {
values.add(cast(val, type));
}
}
} else {
if (value != null) {
value = value.trim();
}
field.set(this, cast(value, type));
}
} catch (Exception e) {
throw ExceptionUtils.uncheck(e);
}
}
protected String[] split(Field field, String value) {
return value.split(",");
}
/**
* 固化配置,对象变成只读。目前只要是处理 List as unmodifiableList
*/
public T build() {
Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
if (Modifier.isFinal(field.getModifiers())) continue;
field.setAccessible(true);
if (List.class.isAssignableFrom(field.getType())) {
try {
List<?> values = (List<?>) field.get(this);
if (values == null) {
values = Collections.EMPTY_LIST;
} else {
values = Collections.unmodifiableList(values);
}
field.set(this, values);
} catch (Exception e) {
throw ExceptionUtils.uncheck(e);
}
}
}
return (T) this;
}
@SuppressWarnings("rawtypes")
private Object cast(String value, Class<?> type) {
if (value == null) {
return value;
}
value = replaceValue(value);
if (String.class.equals(type)) {
return value;
}
if (value == null || value.length() == 0) {
if (Boolean.TYPE.equals(type)) {
return Boolean.FALSE;
} else if (Integer.TYPE.equals(type)) {
return Integer.valueOf(0);
} else if (Long.TYPE.equals(type)) {
return Long.valueOf(value);
} else if (Float.TYPE.equals(type)) {
return Float.valueOf(0);
} else if (Double.TYPE.equals(type)) {
return Double.valueOf(0);
}
return null;
}
if (Boolean.class.equals(type) || Boolean.TYPE.equals(type)) {
value = value.toLowerCase();
return "true".equals(value) || "1".equals(value);
} else if (Integer.class.equals(type) || Integer.TYPE.equals(type)) {
return Integer.valueOf(value);
} else if (Long.class.equals(type) || Long.TYPE.equals(type)) {
return Long.valueOf(value);
} else if (Float.class.equals(type) || Float.TYPE.equals(type)) {
return Float.valueOf(value);
} else if (Double.class.equals(type) || Double.TYPE.equals(type)) {
return Double.valueOf(value);
} else if (Date.class.equals(type)) {
return DateUtils.asDate(value);
} else if (Class.class.equals(type)) {
try {
return ClassLoaderUtils.loadClass(value);
} catch (ClassNotFoundException e) {
throw ExceptionUtils.uncheck(e);
}
} else if (type.isEnum()) {
return Enum.valueOf((Class<Enum>) type, value);
}
return value;
}
// 根据 System.getProperty() 替换变量
private String replaceValue(String value) {
if (value == null) {
return null;
}
if (value.indexOf('$') >= 0) {
Matcher matcher = PARAMETER_PATTERN.matcher(value);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String val = matcher.group(1);
val = System.getProperty(val);
if (val == null) {
val = "";
}
matcher.appendReplacement(sb, Matcher.quoteReplacement(val));
}
matcher.appendTail(sb);
value = sb.toString();
}
return value;
}
/**
* 按照逗号分隔字符串,如果在 openChars 和 closeChars 之间的逗号,则不分割.
*/
protected String[] split(String value, String openChars, String closeChars) {
List<String> results = new ArrayList<String>();
int mode = 0;
int lastpos = 0;
for (int i = 0, len = value.length(); i < len; i++) {
char c = value.charAt(i);
if (openChars.indexOf(c) >= 0) {
mode++;
} else if (closeChars.indexOf(c) >= 0) {
mode--;
} else if (c == ',' && mode == 0) {
results.add(value.substring(lastpos, i));
lastpos = i + 1;
}
}
if (lastpos < value.length()) {
results.add(value.substring(lastpos));
}
return results.toArray(new String[results.size()]);
}
}