jfinal enjoy 3.5

This commit is contained in:
James 2018-10-04 21:38:21 +08:00
parent 49d53e9f55
commit 13f2d302c3
24 changed files with 763 additions and 181 deletions

34
pom.xml
View File

@ -73,8 +73,8 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version> <version>3.6.1</version>
<configuration> <configuration>
<source>1.6</source> <source>1.7</source>
<target>1.6</target> <target>1.7</target>
</configuration> </configuration>
</plugin> </plugin>
@ -91,21 +91,21 @@
</configuration> </configuration>
</plugin> </plugin>
<!-- 安装源码到本地仓库 --> <!-- 安装源码到本地仓库 -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version> <version>2.1.2</version>
<executions> <executions>
<execution> <execution>
<id>attach-sources</id> <id>attach-sources</id>
<phase>verify</phase> <phase>verify</phase>
<goals> <goals>
<goal>jar-no-fork</goal> <goal>jar-no-fork</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

View File

@ -22,6 +22,9 @@ import java.util.Map;
import com.jfinal.kit.HashKit; import com.jfinal.kit.HashKit;
import com.jfinal.kit.StrKit; import com.jfinal.kit.StrKit;
import com.jfinal.kit.SyncWriteMap; import com.jfinal.kit.SyncWriteMap;
import com.jfinal.template.expr.ast.FieldGetter;
import com.jfinal.template.expr.ast.FieldKeyBuilder;
import com.jfinal.template.expr.ast.FieldKit;
import com.jfinal.template.expr.ast.MethodKit; import com.jfinal.template.expr.ast.MethodKit;
import com.jfinal.template.source.ClassPathSourceFactory; import com.jfinal.template.source.ClassPathSourceFactory;
import com.jfinal.template.source.ISource; import com.jfinal.template.source.ISource;
@ -126,7 +129,7 @@ public class Engine {
} }
/** /**
* Get template with file name * Get template by file name
*/ */
public Template getTemplate(String fileName) { public Template getTemplate(String fileName) {
if (fileName.charAt(0) != '/') { if (fileName.charAt(0) != '/') {
@ -172,9 +175,9 @@ public class Engine {
/** /**
* Get template by string content * Get template by string content
* *
* 重要StringSource 中的 key = HashKit.md5(content)也即 key * 重要StringSource 中的 cacheKey = HashKit.md5(content)也即 cacheKey
* content 有紧密的对应关系 content 发生变化时 key 值也相应变化 * content 有紧密的对应关系 content 发生变化时 cacheKey 值也相应变化
* 因此原先 key 所对应的 Template 缓存对象已无法被获取 getTemplateByString(String) * 因此原先 cacheKey 所对应的 Template 缓存对象已无法被获取 getTemplateByString(String)
* String 参数的数量不确定时会引发内存泄漏 * String 参数的数量不确定时会引发内存泄漏
* *
* getTemplateByString(String, boolean) 中的 String 参数的 * getTemplateByString(String, boolean) 中的 String 参数的
@ -188,37 +191,37 @@ public class Engine {
return buildTemplateBySource(new StringSource(content, cache)); return buildTemplateBySource(new StringSource(content, cache));
} }
String key = HashKit.md5(content); String cacheKey = HashKit.md5(content);
Template template = templateCache.get(key); Template template = templateCache.get(cacheKey);
if (template == null) { if (template == null) {
template = buildTemplateBySource(new StringSource(content, cache)); template = buildTemplateBySource(new StringSource(content, cache));
templateCache.put(key, template); templateCache.put(cacheKey, template);
} else if (devMode) { } else if (devMode) {
if (template.isModified()) { if (template.isModified()) {
template = buildTemplateBySource(new StringSource(content, cache)); template = buildTemplateBySource(new StringSource(content, cache));
templateCache.put(key, template); templateCache.put(cacheKey, template);
} }
} }
return template; return template;
} }
/** /**
* Get template with implementation of ISource * Get template by implementation of ISource
*/ */
public Template getTemplate(ISource source) { public Template getTemplate(ISource source) {
String key = source.getKey(); String cacheKey = source.getCacheKey();
if (key == null) { // key null 则不缓存详见 ISource.getKey() 注释 if (cacheKey == null) { // cacheKey null 则不缓存详见 ISource.getCacheKey() 注释
return buildTemplateBySource(source); return buildTemplateBySource(source);
} }
Template template = templateCache.get(key); Template template = templateCache.get(cacheKey);
if (template == null) { if (template == null) {
template = buildTemplateBySource(source); template = buildTemplateBySource(source);
templateCache.put(key, template); templateCache.put(cacheKey, template);
} else if (devMode) { } else if (devMode) {
if (template.isModified()) { if (template.isModified()) {
template = buildTemplateBySource(source); template = buildTemplateBySource(source);
templateCache.put(key, template); templateCache.put(cacheKey, template);
} }
} }
return template; return template;
@ -236,7 +239,7 @@ public class Engine {
} }
/** /**
* Add shared function with file * Add shared function by file
*/ */
public Engine addSharedFunction(String fileName) { public Engine addSharedFunction(String fileName) {
config.addSharedFunction(fileName); config.addSharedFunction(fileName);
@ -252,7 +255,7 @@ public class Engine {
} }
/** /**
* Add shared function with files * Add shared function by files
*/ */
public Engine addSharedFunction(String... fileNames) { public Engine addSharedFunction(String... fileNames) {
config.addSharedFunction(fileNames); config.addSharedFunction(fileNames);
@ -336,7 +339,7 @@ public class Engine {
} }
/** /**
* Remove shared Method with method name * Remove shared Method by method name
*/ */
public Engine removeSharedMethod(String methodName) { public Engine removeSharedMethod(String methodName) {
config.removeSharedMethod(methodName); config.removeSharedMethod(methodName);
@ -360,10 +363,10 @@ public class Engine {
} }
/** /**
* Remove template cache with template key * Remove template cache by cache key
*/ */
public void removeTemplateCache(String templateKey) { public void removeTemplateCache(String cacheKey) {
templateCache.remove(templateKey); templateCache.remove(cacheKey);
} }
/** /**
@ -500,6 +503,45 @@ public class Engine {
public static void removeExtensionMethod(Class<?> targetClass, Class<?> extensionClass) { public static void removeExtensionMethod(Class<?> targetClass, Class<?> extensionClass) {
MethodKit.removeExtensionMethod(targetClass, extensionClass); MethodKit.removeExtensionMethod(targetClass, extensionClass);
} }
/**
* 添加 FieldGetter 实现类到指定的位置
*
* 系统当前默认 FieldGetter 实现类及其位置如下
* GetterMethodFieldGetter ---> 调用 getter 方法取值
* ModelFieldGetter ---> 调用 Model.get(String) 方法取值
* RecordFieldGetter ---> 调用 Record.get(String) 方法取值
* MapFieldGetter ---> 调用 Map.get(String) 方法取值
* RealFieldGetter ---> 直接获取 public 型的 object.field
* ArrayLengthGetter ---> 获取数组长度
*
* 根据以上次序如果要插入 IsMethodFieldGetter GetterMethodFieldGetter
* 之后的代码如下
* Engine.addFieldGetter(1, new IsMethodFieldGetter());
*
* IsMethodFieldGetter 系统已经提供只是默认没有启用该实现类通过调用
* target.isXxx() 方法获取 target.xxx 表达式的值其中 xxx 字段必须是
* Boolean/boolean 类型
*/
public static void addFieldGetter(int index, FieldGetter fieldGetter) {
FieldKit.addFieldGetter(index, fieldGetter);
}
public static void addFieldGetterToLast(FieldGetter fieldGetter) {
FieldKit.addFieldGetterToLast(fieldGetter);
}
public static void addFieldGetterToFirst(FieldGetter fieldGetter) {
FieldKit.addFieldGetterToFirst(fieldGetter);
}
public static void removeFieldGetter(Class<? extends FieldGetter> fieldGetterClass) {
FieldKit.removeFieldGetter(fieldGetterClass);
}
public static void setToFastFieldKeyBuilder() {
FieldKeyBuilder.setToFastFieldKeyBuilder();
}
} }

View File

@ -16,11 +16,8 @@
package com.jfinal.template.expr.ast; package com.jfinal.template.expr.ast;
import java.lang.reflect.Array;
import com.jfinal.kit.HashKit; import com.jfinal.kit.HashKit;
import com.jfinal.kit.StrKit; import com.jfinal.kit.StrKit;
// import com.jfinal.plugin.activerecord.Model;
// import com.jfinal.plugin.activerecord.Record;
import com.jfinal.template.TemplateException; import com.jfinal.template.TemplateException;
import com.jfinal.template.stat.Location; import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParseException; import com.jfinal.template.stat.ParseException;
@ -68,49 +65,26 @@ public class Field extends Expr {
throw new TemplateException("Can not accessed by \"" + fieldName + "\" field from null target", location); throw new TemplateException("Can not accessed by \"" + fieldName + "\" field from null target", location);
} }
Class<?> targetClass = target.getClass();
Long key = buildFieldKey(targetClass);
MethodInfo getter;
try { try {
getter = MethodKit.getGetterMethod(key, targetClass, getterName); Class<?> targetClass = target.getClass();
Object key = FieldKeyBuilder.instance.getFieldKey(targetClass, getterNameHash);
FieldGetter fieldGetter = FieldKit.getFieldGetter(key, targetClass, fieldName);
if (fieldGetter.notNull()) {
return fieldGetter.get(target, fieldName);
}
} catch (TemplateException e) {
throw e;
} catch (ParseException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e); throw new TemplateException(e.getMessage(), location, e);
} }
try {
if (getter != null) {
return getter.invoke(target, ExprList.NULL_OBJECT_ARRAY);
}
// if (target instanceof Model) {
// return ((Model<?>)target).get(fieldName);
// }
// if (target instanceof Record) {
// return ((Record)target).get(fieldName);
// }
if (target instanceof java.util.Map) {
return ((java.util.Map<?, ?>)target).get(fieldName);
}
// if (target instanceof com.jfinal.kit.Ret) {
// return ((com.jfinal.kit.Ret)target).get(fieldName);
// }
java.lang.reflect.Field field = FieldKit.getField(key, targetClass, fieldName);
if (field != null) {
return field.get(target);
}
// 支持获取数组长度 array.length
if ("length".equals(fieldName) && target.getClass().isArray()) {
return Array.getLength(target);
}
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
}
if (scope.getCtrl().isNullSafe()) { if (scope.getCtrl().isNullSafe()) {
return null; return null;
} }
if (expr instanceof Id) { if (expr instanceof Id) {
String id = ((Id)expr).getId(); String id = ((Id)expr).getId();
throw new TemplateException("public field not found: \"" + id + "." + fieldName + "\" and public getter method not found: \"" + id + "." + getterName + "()\"", location); throw new TemplateException("public field not found: \"" + id + "." + fieldName + "\" and public getter method not found: \"" + id + "." + getterName + "()\"", location);
@ -118,9 +92,9 @@ public class Field extends Expr {
throw new TemplateException("public field not found: \"" + fieldName + "\" and public getter method not found: \"" + getterName + "()\"", location); throw new TemplateException("public field not found: \"" + fieldName + "\" and public getter method not found: \"" + getterName + "()\"", location);
} }
private Long buildFieldKey(Class<?> targetClass) { // private Long buildFieldKey(Class<?> targetClass) {
return targetClass.getName().hashCode() ^ getterNameHash; // return targetClass.getName().hashCode() ^ getterNameHash;
} // }
} }

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.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 com.jfinal.template.expr.ast;
/**
* FieldGetter 用于支持 target.field 表达式的取值
* 以及支持用户扩展自定义的 FieldGetter 实现方式
*/
public abstract class FieldGetter {
/**
* 接管 target.fieldName 表达式如果可以接管则返回接管对象否则返回 null
* @param targetClass target.fieldName 表达式中 target Class 类型
* @param fieldName target.fieldName 表达式中的 fieldName 部分
* @return 如果可以接管 targetClass.fieldName 则返回接管对象否则返回 null
*/
public abstract FieldGetter takeOver(Class<?> targetClass, String fieldName);
/**
* 获取 target.fieldName 表达式的值
* @param target 目标对象
* @param fieldName 字段名称
* @return target.fieldName 表达式的值
*/
public abstract Object get(Object target, String fieldName) throws Exception;
/**
* 仅仅 NullFieldGetter 会覆盖此方法并返回 false
*
* 用于消除 FieldKit.getFieldGetter(...) 中的 instanceof 判断
* 并且让 Map fieldGetterCache value 值不必使用 Object 类型
* 还消除了 Field.exec(...) 中的 null 值判断
*/
public boolean notNull() {
return true;
}
}

View File

@ -0,0 +1,270 @@
/**
* Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.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 com.jfinal.template.expr.ast;
import java.lang.reflect.Array;
import com.jfinal.kit.StrKit;
/**
* FieldGetters 封装官方默认 FieldGetter 实现
*/
public class FieldGetters {
/**
* NullFieldGetter
*
* 用于消除 FieldKit.getFieldGetter(...) 中的 instanceof 判断并且让 Map fieldGetterCache
* 中的 value 不必使用 Object 类型还消除了 Field.exec(...) 中的 null 值判断
*/
public static class NullFieldGetter extends FieldGetter {
public static final NullFieldGetter me = new NullFieldGetter();
public boolean notNull() {
return false;
}
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
throw new RuntimeException("The method takeOver(Class, String) of NullFieldGetter should not be invoked");
}
public Object get(Object target, String fieldName) throws Exception {
throw new RuntimeException("The method get(Object, String) of NullFieldGetter should not be invoked");
}
}
/**
* GetterMethodFieldGetter
*
* 使用 getter 方法获取 target.field 表达式的值
*/
public static class GetterMethodFieldGetter extends FieldGetter {
protected java.lang.reflect.Method getterMethod;
public GetterMethodFieldGetter(java.lang.reflect.Method getterMethod) {
this.getterMethod = getterMethod;
}
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
if (MethodKit.isForbiddenClass(targetClass)) {
throw new RuntimeException("Forbidden class: " + targetClass.getName());
}
String getterName = "get" + StrKit.firstCharToUpperCase(fieldName);
java.lang.reflect.Method[] methodArray = targetClass.getMethods();
for (java.lang.reflect.Method method : methodArray) {
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0) {
if (MethodKit.isForbiddenMethod(getterName)) {
throw new RuntimeException("Forbidden method: " + getterName);
}
return new GetterMethodFieldGetter(method);
}
}
return null;
}
public Object get(Object target, String fieldName) throws Exception {
return getterMethod.invoke(target, ExprList.NULL_OBJECT_ARRAY);
}
public String toString() {
return getterMethod.toString();
}
}
/**
* IsMethodFieldGetter
*
* 使用 target.isXxx() 方法获取值默认不启用该功能用户可以通过如下方式启用
* Engine.addLastFieldGetter(new FieldGetters.IsMethodFieldGetter());
*/
public static class IsMethodFieldGetter extends FieldGetter {
protected java.lang.reflect.Method isMethod;
// 此构造方法仅为了方便在 Engine.addFieldGetter(...) 添加时不用为构造方法传参
public IsMethodFieldGetter() {
}
public IsMethodFieldGetter(java.lang.reflect.Method isMethod) {
this.isMethod = isMethod;
}
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
if (MethodKit.isForbiddenClass(targetClass)) {
throw new RuntimeException("Forbidden class: " + targetClass.getName());
}
String isMethodName = "is" + StrKit.firstCharToUpperCase(fieldName);
java.lang.reflect.Method[] methodArray = targetClass.getMethods();
for (java.lang.reflect.Method method : methodArray) {
if (method.getName().equals(isMethodName) && method.getParameterTypes().length == 0) {
Class<?> returnType = method.getReturnType();
if (returnType == Boolean.class || returnType == boolean.class) {
return new IsMethodFieldGetter(method);
}
}
}
return null;
}
public Object get(Object target, String fieldName) throws Exception {
return isMethod.invoke(target, ExprList.NULL_OBJECT_ARRAY);
}
public String toString() {
return isMethod.toString();
}
}
/**
* ModelFieldGetter
*
* 使用 Model.get(String) 获取值
public static class ModelFieldGetter extends FieldGetter {
// 所有 Model 可以共享 ModelFieldGetter 获取属性
static final ModelFieldGetter singleton = new ModelFieldGetter();
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
if (com.jfinal.plugin.activerecord.Model.class.isAssignableFrom(targetClass)) {
return singleton;
} else {
return null;
}
}
public Object get(Object target, String fieldName) throws Exception {
return ((com.jfinal.plugin.activerecord.Model<?>)target).get(fieldName);
}
} */
/**
* RecordFieldGetter
*
* 使用 Record.get(String) 获取值
public static class RecordFieldGetter extends FieldGetter {
// 所有 Record 可以共享 RecordFieldGetter 获取属性
static final RecordFieldGetter singleton = new RecordFieldGetter();
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
if (com.jfinal.plugin.activerecord.Record.class.isAssignableFrom(targetClass)) {
return singleton;
} else {
return null;
}
}
public Object get(Object target, String fieldName) throws Exception {
return ((com.jfinal.plugin.activerecord.Record)target).get(fieldName);
}
} */
/**
* MapFieldGetter
*
* 使用 Map.get(Object) 获取值
*/
public static class MapFieldGetter extends FieldGetter {
// 所有 Map 可以共享 MapFieldGetter 获取属性
static final MapFieldGetter singleton = new MapFieldGetter();
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
if (java.util.Map.class.isAssignableFrom(targetClass)) {
return singleton;
} else {
return null;
}
}
public Object get(Object target, String fieldName) throws Exception {
return ((java.util.Map<?, ?>)target).get(fieldName);
}
}
/**
* RealFieldGetter
*
* 使用 target.field 获取值
*/
public static class RealFieldGetter extends FieldGetter {
protected java.lang.reflect.Field field;
public RealFieldGetter(java.lang.reflect.Field field) {
this.field = field;
}
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
java.lang.reflect.Field[] fieldArray = targetClass.getFields();
for (java.lang.reflect.Field field : fieldArray) {
if (field.getName().equals(fieldName)) {
return new RealFieldGetter(field);
}
}
return null;
}
public Object get(Object target, String fieldName) throws Exception {
return field.get(target);
}
public String toString() {
return field.toString();
}
}
/**
* ArrayLengthGetter
*
* 获取数组长度 array.length
*/
public static class ArrayLengthGetter extends FieldGetter {
// 所有数组可以共享 ArrayLengthGetter 获取属性
static final ArrayLengthGetter singleton = new ArrayLengthGetter();
public FieldGetter takeOver(Class<?> targetClass, String fieldName) {
if ("length".equals(fieldName) && targetClass.isArray()) {
return singleton;
} else {
return null;
}
}
public Object get(Object target, String fieldName) throws Exception {
return Array.getLength(target);
}
}
}

View File

@ -0,0 +1,128 @@
/**
* Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.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 com.jfinal.template.expr.ast;
/**
* FieldKeyBuilder
*
* 用于生成缓存 FieldGetter key
*/
public abstract class FieldKeyBuilder {
public abstract Object getFieldKey(Class<?> targetClass, long fieldFnv1a64Hash);
// 假定是超大规模项目并且假定其 Map/Model/Record + field 组合数量超级庞大默认使用 StrictFieldKeyBuilder
static FieldKeyBuilder instance = new StrictFieldKeyBuilder();
public static FieldKeyBuilder getInstance() {
return instance;
}
/**
* 设置为官方提供的 FastFieldKeyBuilder 实现性能更高
*/
public static void setToFastFieldKeyBuilder() {
instance = new FastFieldKeyBuilder();
}
/**
* 设置为自定义 FieldKeyBuilder
*/
public static void setFieldKeyBuilder(FieldKeyBuilder fieldKeyBuilder) {
if (fieldKeyBuilder == null) {
throw new IllegalArgumentException("fieldKeyBuilder can not be null");
}
instance = fieldKeyBuilder;
}
// ------------------------------------------------------------------------
/**
* FastFieldKeyBuilder
*/
public static class FastFieldKeyBuilder extends FieldKeyBuilder {
public Object getFieldKey(Class<?> targetClass, long fieldFnv1a64Hash) {
return targetClass.getName().hashCode() ^ fieldFnv1a64Hash;
}
}
// ------------------------------------------------------------------------
/**
* StrictFieldKeyBuilder
*/
public static class StrictFieldKeyBuilder extends FieldKeyBuilder {
public Object getFieldKey(Class<?> targetClass, long fieldFnv1a64Hash) {
return new FieldKey(targetClass.getName().hashCode(), fieldFnv1a64Hash);
}
}
// ------------------------------------------------------------------------
/**
* FieldKey
*
* FieldKey 用于封装 targetClassfieldName 这两部分的 hash
* 确保不会出现 key 值碰撞
*
* 这两部分 hash 值在不同 class field 的组合下出现碰撞的
* 概率完全可以忽略不计
*
* 备忘
* 可以考虑用 ThreadLocal 重用 FieldKey 对象但要注意放入 Map fieldGetterCache
* 中的 FieldKey 对象需要 clone 出来确保线程安全由于 FieldKey 占用空间不大
* 所以 ThreadLocal 方案大概率并无优势 ThreadLocal 中获取数据时除了耗时也无法
* 避免创建对象
*/
public static class FieldKey {
final long classHash;
final long fieldHash;
public FieldKey(long classHash, long fieldHash) {
this.classHash = classHash;
this.fieldHash = fieldHash;
}
public int hashCode() {
return (int)(classHash ^ fieldHash);
}
/**
* FieldKey 的核心价值在于此 equals 方法通过比较两部分 hash 避免超大规模场景下可能的 key 值碰撞
*
* 不必判断 if (fieldKey instanceof FieldKey)因为所有 key 类型必须要相同
* 不必判断 if (this == fieldKey)因为每次用于取值的 FieldKey 都是新建的
*/
public boolean equals(Object fieldKey) {
FieldKey fk = (FieldKey)fieldKey;
return classHash == fk.classHash && fieldHash == fk.fieldHash;
}
public String toString() {
return "classHash = " + classHash + "\nfieldHash = " + fieldHash;
}
}
}

View File

@ -16,39 +16,122 @@
package com.jfinal.template.expr.ast; package com.jfinal.template.expr.ast;
import java.lang.reflect.Field;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import com.jfinal.kit.SyncWriteMap; import com.jfinal.kit.SyncWriteMap;
import com.jfinal.template.expr.ast.FieldGetters.*;
/** /**
* FieldKit * FieldKit
*/ */
public class FieldKit { public class FieldKit {
private static final HashMap<Long, Object> fieldCache = new SyncWriteMap<Long, Object>(512, 0.5F); private static FieldGetter[] getters = init();
public static Field getField(Long key, Class<?> targetClass, String fieldName) { private static final HashMap<Object, FieldGetter> fieldGetterCache = new SyncWriteMap<Object, FieldGetter>(1024, 0.25F);
Object field = fieldCache.get(key);
if (field == null) { /**
field = doGetField(targetClass, fieldName); * 初始化官方默认 FieldGetter
if (field != null) { *
fieldCache.put(key, field); * 注意
} else { * 默认不启用 IsMethodFieldGetter用户可以通过下面的代码启用
// 对于不存在的 Field只进行一次获取操作主要为了支持 null safe未来需要考虑内存泄漏风险 * Engine.addLastFieldGetter(new FieldGetters.IsMethodFieldGetter());
fieldCache.put(key, Boolean.FALSE); *
} * 也可以通过直接调用 target.isXxx() 方法来达到与 target.xxx 表达式相同的目的
} */
return field instanceof Field ? (Field)field : null; private static FieldGetter[] init() {
LinkedList<FieldGetter> ret = new LinkedList<FieldGetter>();
ret.addLast(new GetterMethodFieldGetter(null));
// ret.addLast(new ModelFieldGetter());
// ret.addLast(new RecordFieldGetter());
ret.addLast(new MapFieldGetter());
ret.addLast(new RealFieldGetter(null));
ret.addLast(new ArrayLengthGetter());
// ret.addLast(new IsMethodFieldGetter());
return ret.toArray(new FieldGetter[ret.size()]);
} }
private static Field doGetField(Class<?> targetClass, String fieldName) { public static FieldGetter getFieldGetter(Object key, Class<?> targetClass, String fieldName) {
Field[] fs = targetClass.getFields(); FieldGetter fieldGetter = fieldGetterCache.get(key);
for (Field f : fs) { if (fieldGetter == null) {
if (f.getName().equals(fieldName)) { fieldGetter = doGetFieldGetter(targetClass, fieldName); // 已确保不会返回 null
return f; fieldGetterCache.putIfAbsent(key, fieldGetter);
}
return fieldGetter;
}
private static FieldGetter doGetFieldGetter(Class<?> targetClass, String fieldName) {
FieldGetter ret;
for (FieldGetter fieldGetter : getters) {
ret = fieldGetter.takeOver(targetClass, fieldName);
if (ret != null) {
return ret;
} }
} }
return null; return NullFieldGetter.me;
}
public static void addFieldGetter(int index, FieldGetter fieldGetter) {
addFieldGetter(fieldGetter, index, true);
}
public static void addFieldGetterToLast(FieldGetter fieldGetter) {
addFieldGetter(fieldGetter, null, true);
}
public static void addFieldGetterToFirst(FieldGetter fieldGetter) {
addFieldGetter(fieldGetter, null, false);
}
// Integer index 不为 null boolean addLast 为无效参数
private static synchronized void addFieldGetter(FieldGetter fieldGetter, Integer index, boolean addLast) {
checkParameter(fieldGetter);
LinkedList<FieldGetter> ret = getCurrentFieldGetters();
if (index != null) {
ret.add(index, fieldGetter);
} else {
if (addLast) {
ret.addLast(fieldGetter);
} else {
ret.addFirst(fieldGetter);
}
}
getters = ret.toArray(new FieldGetter[ret.size()]);
}
private static LinkedList<FieldGetter> getCurrentFieldGetters() {
LinkedList<FieldGetter> ret = new LinkedList<FieldGetter>();
for (FieldGetter fieldGetter : getters) {
ret.add(fieldGetter);
}
return ret;
}
private static void checkParameter(FieldGetter fieldGetter) {
if (fieldGetter == null) {
throw new IllegalArgumentException("The parameter fieldGetter can not be null");
}
for (FieldGetter fg : getters) {
if (fg.getClass() == fieldGetter.getClass()) {
throw new RuntimeException("FieldGetter already exists : " + fieldGetter.getClass().getName());
}
}
}
public static synchronized void removeFieldGetter(Class<? extends FieldGetter> fieldGetterClass) {
LinkedList<FieldGetter> ret = getCurrentFieldGetters();
for (Iterator<FieldGetter> it = ret.iterator(); it.hasNext();) {
if (it.next().getClass() == fieldGetterClass) {
it.remove();
}
}
getters = ret.toArray(new FieldGetter[ret.size()]);
} }
} }
@ -56,3 +139,6 @@ public class FieldKit {

View File

@ -33,7 +33,7 @@ import com.jfinal.template.stat.Scope;
* 如果在未来通过结合 #dynamic(boolean) 指令来优化需要在 Ctrl 中引入一个 * 如果在未来通过结合 #dynamic(boolean) 指令来优化需要在 Ctrl 中引入一个
* boolean dynamic = false 变量而不能在 EnvScope 引入该变量 * boolean dynamic = false 变量而不能在 EnvScope 引入该变量
* *
* 还需要引入一个 NullMethodInfo 以及 isNull() 方法此优化复杂度提高不少 * 还需要引入一个 NullMethodInfo 以及 notNull() 方法此优化复杂度提高不少
* 暂时不做此优化 * 暂时不做此优化
*/ */
public class Method extends Expr { public class Method extends Expr {
@ -76,28 +76,26 @@ public class Method extends Expr {
} }
Object[] argValues = exprList.evalExprList(scope); Object[] argValues = exprList.evalExprList(scope);
MethodInfo methodInfo;
try { try {
methodInfo = MethodKit.getMethod(target.getClass(), methodName, argValues);
} catch (Exception e) { MethodInfo methodInfo = MethodKit.getMethod(target.getClass(), methodName, argValues);
throw new TemplateException(e.getMessage(), location, e); if (methodInfo != null) {
} return methodInfo.invoke(target, argValues);
if (methodInfo == null) { }
if (scope.getCtrl().isNullSafe()) { if (scope.getCtrl().isNullSafe()) {
return null; return null;
} }
throw new TemplateException(buildMethodNotFoundSignature("public method not found: " + target.getClass().getName() + ".", methodName, argValues), location); throw new TemplateException(buildMethodNotFoundSignature("public method not found: " + target.getClass().getName() + ".", methodName, argValues), location);
}
try { } catch (TemplateException e) {
return methodInfo.invoke(target, argValues); throw e;
} catch (ParseException e) {
throw e;
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
Throwable t = e.getTargetException(); Throwable t = e.getTargetException();
if (t != null) { if (t == null) {t = e;}
throw new TemplateException(t.getMessage(), location, t); throw new TemplateException(t.getMessage(), location, t);
} else {
throw new TemplateException(e.getMessage(), location, e);
}
} catch (Exception e) { } catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e); throw new TemplateException(e.getMessage(), location, e);
} }

View File

@ -17,7 +17,6 @@
package com.jfinal.template.expr.ast; package com.jfinal.template.expr.ast;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
@ -41,7 +40,7 @@ public class MethodInfo {
this.paraTypes = method.getParameterTypes(); this.paraTypes = method.getParameterTypes();
} }
public Object invoke(Object target, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { public Object invoke(Object target, Object... args) throws ReflectiveOperationException {
if (isVarArgs) { if (isVarArgs) {
return invokeVarArgsMethod(target, args); return invokeVarArgsMethod(target, args);
} else { } else {
@ -49,7 +48,7 @@ public class MethodInfo {
} }
} }
protected Object invokeVarArgsMethod(Object target, Object[] argValues) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { protected Object invokeVarArgsMethod(Object target, Object[] argValues) throws ReflectiveOperationException {
Object[] finalArgValues = new Object[paraTypes.length]; Object[] finalArgValues = new Object[paraTypes.length];
int fixedParaLength = paraTypes.length - 1; int fixedParaLength = paraTypes.length - 1;

View File

@ -16,7 +16,6 @@
package com.jfinal.template.expr.ast; package com.jfinal.template.expr.ast;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**
@ -37,7 +36,7 @@ public class MethodInfoExt extends MethodInfo {
// this.paraTypes = newParaTypes; // this.paraTypes = newParaTypes;
} }
public Object invoke(Object target, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { public Object invoke(Object target, Object... args) throws ReflectiveOperationException {
Object[] finalArgs = new Object[args.length + 1]; Object[] finalArgs = new Object[args.length + 1];
finalArgs[0] = target; finalArgs[0] = target;

View File

@ -43,17 +43,20 @@ public abstract class MethodKeyBuilder {
* 如果希望将 configEngine(Engine me) 中的 Engine 切换到 StrictMethodKeyBuilder * 如果希望将 configEngine(Engine me) 中的 Engine 切换到 StrictMethodKeyBuilder
* 需要在 YourJFinalConfig extends JFinalConfig 中利用如下代码块才能生效 * 需要在 YourJFinalConfig extends JFinalConfig 中利用如下代码块才能生效
* static { * static {
* MethodKeyBuilder.useStrictMethodKeyBuilder(); * MethodKeyBuilder.setToStrictMethodKeyBuilder();
* } * }
* *
* 原因是在 com.jfinal.core.Config new Engine() useStrictMethodKeyBuilder() * 原因是在 com.jfinal.core.Config new Engine() setToStrictMethodKeyBuilder()
* 方法并未生效所以 extension method 生成 method key 时仍然使用的是 FastMethodKeyBuilder * 方法并未生效所以 extension method 生成 method key 时仍然使用的是 FastMethodKeyBuilder
* 以至于在运行时使用 StrictMethodKeyBuilder 生成的 key 找不到 extension method * 以至于在运行时使用 StrictMethodKeyBuilder 生成的 key 找不到 extension method
* *
* 后续版本考虑在调用 setToStrictMethodKeyBuilder() 以后重新初始化一下 MethodKit 中的变量
* 原先的 static 初始化方式重构出 synchronized void init() 方法来方便调用
*
* </pre> * </pre>
*/ */
public static void useStrictMethodKeyBuilder() { public static void setToStrictMethodKeyBuilder() {
MethodKeyBuilder.instance = new StrictMethodKeyBuilder(); instance = new StrictMethodKeyBuilder();
} }
/** /**
@ -63,7 +66,7 @@ public abstract class MethodKeyBuilder {
if (methodKeyBuilder == null) { if (methodKeyBuilder == null) {
throw new IllegalArgumentException("methodKeyBuilder can not be null"); throw new IllegalArgumentException("methodKeyBuilder can not be null");
} }
MethodKeyBuilder.instance = methodKeyBuilder; instance = methodKeyBuilder;
} }
/** /**

View File

@ -41,7 +41,7 @@ public class MethodKit {
private static final Set<String> forbiddenMethods = new HashSet<String>(64); private static final Set<String> forbiddenMethods = new HashSet<String>(64);
private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>(64); private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>(64);
private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>(64); private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>(64);
private static final Map<Long, Object> methodCache = new SyncWriteMap<Long, Object>(2048, 0.25F); private static final SyncWriteMap<Long, Object> methodCache = new SyncWriteMap<Long, Object>(2048, 0.25F);
// 初始化在模板中调用 method 时所在的被禁止使用类 // 初始化在模板中调用 method 时所在的被禁止使用类
static { static {
@ -94,6 +94,10 @@ public class MethodKit {
return forbiddenClasses.contains(clazz); return forbiddenClasses.contains(clazz);
} }
public static void addForbiddenClass(Class<?> clazz) {
forbiddenClasses.add(clazz);
}
public static boolean isForbiddenMethod(String methodName) { public static boolean isForbiddenMethod(String methodName) {
return forbiddenMethods.contains(methodName); return forbiddenMethods.contains(methodName);
} }
@ -109,10 +113,10 @@ public class MethodKit {
if (method == null) { if (method == null) {
method = doGetMethod(key, targetClass, methodName, argTypes); method = doGetMethod(key, targetClass, methodName, argTypes);
if (method != null) { if (method != null) {
methodCache.put(key, method); methodCache.putIfAbsent(key, method);
} else { } else {
// 对于不存在的 Method只进行一次获取操作主要为了支持 null safe未来需要考虑内存泄漏风险 // 对于不存在的 Method只进行一次获取操作主要为了支持 null safe未来需要考虑内存泄漏风险
methodCache.put(key, Boolean.FALSE); methodCache.putIfAbsent(key, Void.class);
} }
} }
return method instanceof MethodInfo ? (MethodInfo)method : null; return method instanceof MethodInfo ? (MethodInfo)method : null;
@ -121,19 +125,19 @@ public class MethodKit {
/** /**
* 获取 getter 方法 * 获取 getter 方法
* 使用与 Field 相同的 key避免生成两次 key值 * 使用与 Field 相同的 key避免生成两次 key值
*/ * ---> jfinal 3.5 已将此功能转移至 FieldKit
public static MethodInfo getGetterMethod(Long key, Class<?> targetClass, String methodName) { public static MethodInfo getGetterMethod(Long key, Class<?> targetClass, String methodName) {
Object getterMethod = methodCache.get(key); Object getterMethod = methodCache.get(key);
if (getterMethod == null) { if (getterMethod == null) {
getterMethod = doGetMethod(key, targetClass, methodName, NULL_ARG_TYPES); getterMethod = doGetMethod(key, targetClass, methodName, NULL_ARG_TYPES);
if (getterMethod != null) { if (getterMethod != null) {
methodCache.put(key, getterMethod); methodCache.putIfAbsent(key, getterMethod);
} else { } else {
methodCache.put(key, Boolean.FALSE); methodCache.putIfAbsent(key, Void.class);
} }
} }
return getterMethod instanceof MethodInfo ? (MethodInfo)getterMethod : null; return getterMethod instanceof MethodInfo ? (MethodInfo)getterMethod : null;
} } */
static Class<?>[] getArgTypes(Object[] argValues) { static Class<?>[] getArgTypes(Object[] argValues) {
if (argValues == null || argValues.length == 0) { if (argValues == null || argValues.length == 0) {
@ -280,7 +284,7 @@ public class MethodKit {
} }
MethodInfoExt mie = new MethodInfoExt(objectOfExtensionClass, key, extensionClass/* targetClass */, method); MethodInfoExt mie = new MethodInfoExt(objectOfExtensionClass, key, extensionClass/* targetClass */, method);
methodCache.put(key, mie); methodCache.putIfAbsent(key, mie);
} }
} }
} }

View File

@ -26,9 +26,14 @@ import com.jfinal.template.stat.Scope;
* SharedMethod * SharedMethod
* *
* 用法 * 用法
* engine.addSharedMethod(object); * engine.addSharedMethod(new StrKit());
* engine.addSharedStaticMethod(Xxx.class); * engine.addSharedStaticMethod(MyKit.class);
* #(method(para)) *
* #if (notBlank(para))
* ....
* #end
*
* 上面代码中的 notBlank 方法来自 StrKit
*/ */
public class SharedMethod extends Expr { public class SharedMethod extends Expr {
@ -48,14 +53,20 @@ public class SharedMethod extends Expr {
public Object eval(Scope scope) { public Object eval(Scope scope) {
Object[] argValues = exprList.evalExprList(scope); Object[] argValues = exprList.evalExprList(scope);
SharedMethodInfo sharedMethodInfo = sharedMethodKit.getSharedMethodInfo(methodName, argValues);
// ShareMethod 相当于是固定的静态的方法不支持 null safenull safe 只支持具有动态特征的用法
if (sharedMethodInfo == null) {
throw new TemplateException(Method.buildMethodNotFoundSignature("Shared method not found: ", methodName, argValues), location);
}
try { try {
return sharedMethodInfo.invoke(argValues); SharedMethodInfo sharedMethodInfo = sharedMethodKit.getSharedMethodInfo(methodName, argValues);
if (sharedMethodInfo != null) {
return sharedMethodInfo.invoke(argValues);
} else {
// ShareMethod 相当于是固定的静态的方法不支持 null safenull safe 只支持具有动态特征的用法
throw new TemplateException(Method.buildMethodNotFoundSignature("Shared method not found: ", methodName, argValues), location);
}
} catch (TemplateException e) {
throw e;
} catch (ParseException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e); throw new TemplateException(e.getMessage(), location, e);
} }

View File

@ -22,7 +22,6 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.HashMap; import java.util.HashMap;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import com.jfinal.kit.HashKit; import com.jfinal.kit.HashKit;
@ -54,9 +53,9 @@ public class SharedMethodKit {
if (method == null) { if (method == null) {
method = doGetSharedMethodInfo(methodName, argTypes); method = doGetSharedMethodInfo(methodName, argTypes);
if (method != null) { if (method != null) {
methodCache.put(key, method); methodCache.putIfAbsent(key, method);
} }
// shared method 不支持 null safe不缓存: methodCache.put(key, Boolean.FALSE) // shared method 不支持 null safe不缓存: methodCache.putIfAbsent(key, Void.class)
} }
return method; return method;
} }
@ -174,7 +173,7 @@ public class SharedMethodKit {
this.target = target; this.target = target;
} }
public Object invoke(Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { public Object invoke(Object... args) throws ReflectiveOperationException {
return super.invoke(target, args); return super.invoke(target, args);
} }

View File

@ -57,23 +57,25 @@ public class StaticMethod extends Expr {
public Object eval(Scope scope) { public Object eval(Scope scope) {
Object[] argValues = exprList.evalExprList(scope); Object[] argValues = exprList.evalExprList(scope);
MethodInfo methodInfo;
try {
methodInfo = MethodKit.getMethod(clazz, methodName, argValues);
} catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e);
}
// StaticMethod 是固定的存在不支持 null safenull safe 只支持具有动态特征的用法
if (methodInfo == null) {
throw new TemplateException(Method.buildMethodNotFoundSignature("public static method not found: " + clazz.getName() + "::", methodName, argValues), location);
}
if (!methodInfo.isStatic()) {
throw new TemplateException(Method.buildMethodNotFoundSignature("Not public static method: " + clazz.getName() + "::", methodName, argValues), location);
}
try { try {
return methodInfo.invoke(null, argValues); MethodInfo methodInfo = MethodKit.getMethod(clazz, methodName, argValues);
if (methodInfo != null) {
if (methodInfo.isStatic()) {
return methodInfo.invoke(null, argValues);
} else {
throw new TemplateException(Method.buildMethodNotFoundSignature("Not public static method: " + clazz.getName() + "::", methodName, argValues), location);
}
} else {
// StaticMethod 是固定的存在不支持 null safenull safe 只支持具有动态特征的用法
throw new TemplateException(Method.buildMethodNotFoundSignature("public static method not found: " + clazz.getName() + "::", methodName, argValues), location);
}
} catch (TemplateException e) {
throw e;
} catch (ParseException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new TemplateException(e.getMessage(), location, e); throw new TemplateException(e.getMessage(), location, e);
} }

View File

@ -120,7 +120,8 @@ public class RenderDirective extends Directive {
} }
} }
subStat.stat.exec(subStat.env, scope, writer); subStat.exec(null, scope, writer); // subStat.stat.exec(subStat.env, scope, writer);
scope.getCtrl().setJumpNone(); scope.getCtrl().setJumpNone();
} }
@ -138,16 +139,21 @@ public class RenderDirective extends Directive {
} }
} }
private static class SubStat { public static class SubStat extends Stat {
SubEnv env; public SubEnv env;
Stat stat; public Stat stat;
ISource source; public ISource source;
SubStat(SubEnv env, Stat stat, ISource source) { public SubStat(SubEnv env, Stat stat, ISource source) {
this.env = env; this.env = env;
this.stat = stat; this.stat = stat;
this.source = source; this.source = source;
} }
@Override
public void exec(Env env, Scope scope, Writer writer) {
stat.exec(this.env, scope, writer);
}
} }
/** /**
@ -160,8 +166,8 @@ public class RenderDirective extends Directive {
* *
* 注意 #render 子模板中定义的模板函数无法在父模板中调用 * 注意 #render 子模板中定义的模板函数无法在父模板中调用
*/ */
private static class SubEnv extends Env { public static class SubEnv extends Env {
Env parentEnv; public Env parentEnv;
public SubEnv(Env parentEnv) { public SubEnv(Env parentEnv) {
super(parentEnv.getEngineConfig()); super(parentEnv.getEngineConfig());
@ -171,6 +177,7 @@ public class RenderDirective extends Directive {
/** /**
* 接管父类 getFunction()先从子模板中找模板函数找不到再去父模板中找 * 接管父类 getFunction()先从子模板中找模板函数找不到再去父模板中找
*/ */
@Override
public Define getFunction(String functionName) { public Define getFunction(String functionName) {
Define func = functionMap.get(functionName); Define func = functionMap.get(functionName);
return func != null ? func : parentEnv.getFunction(functionName); return func != null ? func : parentEnv.getFunction(functionName);

View File

@ -22,7 +22,7 @@ package com.jfinal.template.io;
public class WriterBuffer { public class WriterBuffer {
private static final int MIN_BUFFER_SIZE = 64; // 缓冲区最小 64 字节 private static final int MIN_BUFFER_SIZE = 64; // 缓冲区最小 64 字节
private static final int MAX_BUFFER_SIZE = 1024 * 1024 * 10; // 缓冲区最大 10M 字节 private static final int MAX_BUFFER_SIZE = 1024 * 1024 * 10; // 缓冲区最大 10M 字节
private int bufferSize = 2048; // 缓冲区大小 private int bufferSize = 2048; // 缓冲区大小

View File

@ -105,7 +105,7 @@ public class ClassPathSource implements ISource {
return finalFileName; return finalFileName;
} }
public String getKey() { public String getCacheKey() {
return fileName; return fileName;
} }

View File

@ -48,7 +48,7 @@ public class FileSource implements ISource {
return lastModified != new File(finalFileName).lastModified(); return lastModified != new File(finalFileName).lastModified();
} }
public String getKey() { public String getCacheKey() {
return fileName; return fileName;
} }

View File

@ -27,12 +27,12 @@ public interface ISource {
boolean isModified(); boolean isModified();
/** /**
* key used to cache, return null if do not cache the template * cache key used to cache, return null if do not cache the template
* *
* 注意如果不希望缓存从该 ISource 解析出来的 Template 对象 * 注意如果不希望缓存从该 ISource 解析出来的 Template 对象
* getKey() 返回 null 值即可 * getCacheKey() 返回 null 值即可
*/ */
String getKey(); String getCacheKey();
/** /**
* content of ISource * content of ISource

View File

@ -25,7 +25,7 @@ import com.jfinal.template.EngineConfig;
*/ */
public class StringSource implements ISource { public class StringSource implements ISource {
private String key; private String cacheKey;
private StringBuilder content; private StringBuilder content;
/** /**
@ -38,7 +38,7 @@ public class StringSource implements ISource {
throw new IllegalArgumentException("content can not be blank"); throw new IllegalArgumentException("content can not be blank");
} }
this.content = new StringBuilder(content); this.content = new StringBuilder(content);
this.key = cache ? HashKit.md5(content) : null; // 不缓存只要将 key 值赋为 null 即可 this.cacheKey = cache ? HashKit.md5(content) : null; // 不缓存只要将 cacheKey 值赋为 null 即可
} }
public StringSource(StringBuilder content, boolean cache) { public StringSource(StringBuilder content, boolean cache) {
@ -46,15 +46,15 @@ public class StringSource implements ISource {
throw new IllegalArgumentException("content can not be blank"); throw new IllegalArgumentException("content can not be blank");
} }
this.content = content; this.content = content;
this.key = cache ? HashKit.md5(content.toString()) : null; // 不缓存只要将 key 值赋为 null 即可 this.cacheKey = cache ? HashKit.md5(content.toString()) : null; // 不缓存只要将 cacheKey 值赋为 null 即可
} }
public boolean isModified() { public boolean isModified() {
return false; return false;
} }
public String getKey() { public String getCacheKey() {
return key; return cacheKey;
} }
public StringBuilder getContent() { public StringBuilder getContent() {
@ -67,8 +67,8 @@ public class StringSource implements ISource {
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("Key : ").append(key).append("\n"); sb.append("cacheKey : ").append(cacheKey).append("\n");
sb.append("Content : ").append(content).append("\n"); sb.append("content : ").append(content).append("\n");
return sb.toString(); return sb.toString();
} }
} }

View File

@ -90,7 +90,7 @@ public class Parser {
tokenList.add(EOF); tokenList.add(EOF);
StatList statList = statList(); StatList statList = statList();
if (peek() != EOF) { if (peek() != EOF) {
throw new ParseException("Syntax error: can not match " + peek().value(), getLocation(peek().row)); throw new ParseException("Syntax error: can not match \"#" + peek().value() + "\"", getLocation(peek().row));
} }
return statList; return statList;
} }

View File

@ -73,6 +73,8 @@ public class Output extends Stat {
} }
} catch(TemplateException e) { } catch(TemplateException e) {
throw e; throw e;
} catch(ParseException e) {
throw e;
} catch(Exception e) { } catch(Exception e) {
throw new TemplateException(e.getMessage(), location, e); throw new TemplateException(e.getMessage(), location, e);
} }

View File

@ -27,7 +27,7 @@ import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope; import com.jfinal.template.stat.Scope;
/** /**
* SetLocal 设置全局变量全局作用域是指本次请求的整个 template * SetGlobal 设置全局变量全局作用域是指本次请求的整个 template
* *
* 适用于极少数的在内层作用域中希望直接操作顶层作用域的场景 * 适用于极少数的在内层作用域中希望直接操作顶层作用域的场景
*/ */