55 Commits

Author SHA1 Message Date
James
2224e0d212 [maven-release-plugin] prepare release enjoy-4.2 2019-06-02 22:07:53 +08:00
James
1a6006fe36 jfinal enjoy 4.2 2019-06-02 22:04:57 +08:00
James
12c3b5fdb5 jfinal enjoy 4.1 release ^_^ 2019-05-30 22:52:36 +08:00
James
f065faf4e7 [maven-release-plugin] prepare for next development iteration 2019-05-29 21:56:23 +08:00
James
b4e277fcc0 [maven-release-plugin] prepare release enjoy-4.1 2019-05-29 21:56:19 +08:00
James
bbc1e24b87 [maven-release-plugin] prepare for next development iteration 2019-05-21 22:57:57 +08:00
James
34c1a9e53a [maven-release-plugin] prepare release enjoy-4.0 2019-05-21 22:57:51 +08:00
James
f6a855b6bf enjoy 4.0 release ^_^ 2019-05-21 22:52:26 +08:00
James
4377d19e2f [maven-release-plugin] prepare for next development iteration 2019-04-07 22:30:17 +08:00
James
820f2806ec [maven-release-plugin] prepare release enjoy-3.8 2019-04-07 22:30:15 +08:00
James
808bdf6079 enjoy 3.8 2019-04-07 22:23:49 +08:00
James
eac1d8d055 enjoy 3.8 2019-04-07 21:45:28 +08:00
James
d5a88b8be4 enjoy 3.8 2019-04-07 21:36:00 +08:00
James
fd5d554171 enjoy 3.8 2019-04-07 21:34:21 +08:00
James
6d18be3df8 [maven-release-plugin] prepare for next development iteration 2019-03-19 16:31:01 +08:00
James
869824e2bb [maven-release-plugin] prepare release enjoy-3.7 2019-03-19 16:30:57 +08:00
James
84573be584 enjoy 3.7 release ^_^ 2019-03-19 16:25:33 +08:00
James
3cc94a5b32 enjoy 3.7 release 2019-03-19 16:23:21 +08:00
James
3a4f4f4495 enjoy 3.7 2019-02-25 18:44:34 +08:00
James
d250b431a4 jfinal enjoy 3.6 release ^_^ 2019-01-31 16:45:57 +08:00
James
5e133e7de5 jfinal enjoy 3.6 release ^_^ 2019-01-31 16:45:09 +08:00
James
ef39843a25 jfinal enjoy 3.6 release ^_^ 2019-01-31 16:43:49 +08:00
James
0e5f3b7249 [maven-release-plugin] prepare for next development iteration 2019-01-31 00:15:29 +08:00
James
6f5cd47376 [maven-release-plugin] prepare release enjoy-3.6 2019-01-31 00:15:25 +08:00
James
b23a1a9133 enjoy 3.6 release ^_^ 2019-01-30 21:09:51 +08:00
James
4c63d00157 enjoy 3.6 release ^_^ 2019-01-30 21:09:07 +08:00
James
972c7e7673 enjoy 3.6 2019-01-01 21:17:38 +08:00
James
6d7d0af2b2 [maven-release-plugin] prepare for next development iteration 2018-10-08 17:42:32 +08:00
James
6bf9ae3f59 [maven-release-plugin] prepare release enjoy-3.5 2018-10-08 17:42:28 +08:00
James
d46ca53a34 jfinal enjoy 3.5 2018-10-08 17:34:53 +08:00
James
985b02177e jfinal enjoy 3.5 2018-10-06 17:17:13 +08:00
James
b9cc8a58e0 jfinal enjoy 3.5 2018-10-04 21:47:18 +08:00
James
31ca22d21d jfinal enjoy 3.5 2018-10-04 21:44:17 +08:00
James
13f2d302c3 jfinal enjoy 3.5 2018-10-04 21:38:21 +08:00
James
49d53e9f55 enjoy 3.5 2018-08-22 23:43:31 +08:00
James
8d88d0bba4 enjoy 3.5 2018-08-14 23:11:21 +08:00
James
462c70b692 enjoy 3.5 2018-08-12 12:16:41 +08:00
James
1ce7068072 enjoy 3.5 2018-08-12 12:15:01 +08:00
James
3e89651aa4 [maven-release-plugin] prepare for next development iteration 2018-04-27 12:18:46 +08:00
James
1aef12eb94 [maven-release-plugin] prepare release enjoy-3.4 2018-04-27 12:18:43 +08:00
James
6f615bc1a8 enjoy 3.4 release ^_^ 2018-04-25 22:10:00 +08:00
James
0c3e7790b7 enjoy 3.4 release ^_^ 2018-04-25 22:09:05 +08:00
James
a1e88a4a4c enjoy 3.4 2018-04-02 19:14:24 +08:00
James
d12d3d8abb jfinal 3.4 2018-04-02 14:15:59 +08:00
James
ef7b0da917 enjoy 3.4 2018-04-01 16:29:13 +08:00
James
2ed806c296 enjoy 3.4 2018-03-30 17:06:02 +08:00
James
998df7b907 enjoy 3.4 2018-03-30 02:51:56 +08:00
James
3e93fc970e jfinal enjoy 3.4 2018-03-02 23:41:05 +08:00
James
010a7f3d8a [maven-release-plugin] prepare for next development iteration 2017-12-25 11:47:53 +08:00
James
99f024f546 [maven-release-plugin] prepare release enjoy-3.4 2017-12-25 11:47:51 +08:00
James
ca3361703e enjoy 3.4 2017-12-24 23:37:35 +08:00
James
1840d49e0a [maven-release-plugin] prepare for next development iteration 2017-12-24 22:46:28 +08:00
James
70d1e5a9eb [maven-release-plugin] prepare release enjoy-3.4 2017-12-24 22:46:25 +08:00
James
e0abc218b1 Map 定义初始化表达式,添加支持 key 可以为 int、long、float、double、true、false、null 常量 2017-12-10 19:40:40 +08:00
James
bd1949fc56 [maven-release-plugin] prepare for next development iteration 2017-11-21 23:14:12 +08:00
129 changed files with 2125 additions and 916 deletions

2
.gitignore vendored
View File

@@ -50,3 +50,5 @@ integration-repo
a_little_config_pro.txt a_little_config_pro.txt
dev_plan.txt dev_plan.txt

View File

@@ -189,3 +189,8 @@ third-party archives.
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.

View File

@@ -1,16 +1,26 @@
### Enjoy ### Enjoy
Enjoy 是基于 Java 语言的极轻量极魔板引擎。极轻量级仅 171K 并且不依赖任何第三方。极简设计仅 if、for、set、define、include、render 个核心指令,让学习成本低到极致。独创 DKFF(Dynamic Key Feature Forward) 词法分析算法与 DLRD (Double Layer Recursive Descent)语法分析算法,避免使用 javacc、antlr、jflex 生成器,令代码量少到极致。 Enjoy 是基于 Java 语言的极轻量极魔板引擎。极轻量级仅 171K 并且不依赖任何第三方。极简设计仅 if、for、switch、set、define、include、render 个核心指令,让学习成本低到极致。独创 DKFF(Dynamic Key Feature Forward) 词法分析算法与 DLRD (Double Layer Recursive Descent)语法分析算法,避免使用 javacc、antlr、jflex 生成器,令代码量少到极致。
#### Maven 坐标
```java
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>enjoy</artifactId>
<version>4.2</version>
</dependency>
```
#### Enjoy 主要特点 #### Enjoy 主要特点
- 消灭传统模板引擎中大量繁杂概念,仅个核心指令,学习成本极低 - 消灭传统模板引擎中大量繁杂概念,仅个核心指令,学习成本极低
- 独创 DKFF 词法分析算法与 DLRD 语法分析算法避免使用javacc、antlr - 独创 DKFF 词法分析算法与 DLRD 语法分析算法,避免使用 javacc、antlr
- 功能强大,极为简单覆盖掉 freemarker、velocity 的核心功能
- 扩展性强,支持多种扩展方式,且是唯一支持指令级扩展的模板引擎
- 与 java 打通式设计,在模板中与 java 交互极为方便 - 与 java 打通式设计,在模板中与 java 交互极为方便
- 贴近 java 使用直觉,为 java 开发者量身打造 - 贴近 java 使用直觉,为 java 开发者量身打造
- 功能强大,极为简单覆盖掉 freemarker、velocity 的核心功能
- 扩展性强,支持多种扩展方式,且是唯一支持指令级扩展的模板引擎
- 回归模板引擎渲染 View 数据的本质,采用指令式设计,避免 view 层表达复杂逻辑 - 回归模板引擎渲染 View 数据的本质,采用指令式设计,避免 view 层表达复杂逻辑
- 体积小,仅 171K,且不依赖于任何第三方 - 体积小,仅 227 KB,且不依赖于任何第三方
#### 简单示例: #### 简单示例:
@@ -34,9 +44,12 @@ Enjoy 是基于 Java 语言的极轻量极魔板引擎。极轻量级仅 171K
``` ```
**2.详细使用方法见 jfinal 手册** **2.详细使用方法见 jfinal 手册**
read me 正在补充,详细使用文档请下载 jfinal.com 官网的 jfinal 手册[http://www.jfinal.com](http://www.jfinal.com)
**JFinal 官方网站[http://www.jfinal.com](http://www.jfinal.com)** read me 正在补充,详细使用文档见官网[https://www.jfinal.com/doc/6-1](https://www.jfinal.com/doc/6-1)
**JFinal Enjoy 官方文档:[https://www.jfinal.com/doc/6-1](https://www.jfinal.com/doc/6-1)**

44
pom.xml
View File

@@ -4,7 +4,7 @@
<artifactId>enjoy</artifactId> <artifactId>enjoy</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>enjoy</name> <name>enjoy</name>
<version>3.3</version> <version>4.2</version>
<url>http://www.jfinal.com</url> <url>http://www.jfinal.com</url>
<description>Enjoy is a simple, light, rapid, independent, extensible Java Template Engine.</description> <description>Enjoy is a simple, light, rapid, independent, extensible Java Template Engine.</description>
@@ -15,7 +15,7 @@
<issueManagement> <issueManagement>
<system>Git Issue</system> <system>Git Issue</system>
<url>http://git.oschina.net/jfinal/enjoy/issues</url> <url>https://gitee.com/jfinal/enjoy/issues</url>
</issueManagement> </issueManagement>
<licenses> <licenses>
<license> <license>
@@ -32,9 +32,9 @@
</developer> </developer>
</developers> </developers>
<scm> <scm>
<connection>scm:git:git@git.oschina.net:jfinal/enjoy.git</connection> <connection>scm:git:git@gitee.com:jfinal/enjoy.git</connection>
<developerConnection>scm:git:git@git.oschina.net:jfinal/enjoy.git</developerConnection> <developerConnection>scm:git:git@gitee.com:jfinal/enjoy.git</developerConnection>
<url>git@git.oschina.net:jfinal/enjoy.git</url> <url>git@gitee.com:jfinal/enjoy.git</url>
</scm> </scm>
<parent> <parent>
@@ -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.8</source>
<target>1.6</target> <target>1.8</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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ public class ElKit {
engine.addDirective("eval", InnerEvalDirective.class); engine.addDirective("eval", InnerEvalDirective.class);
} }
public Engine getEngine() { public static Engine getEngine() {
return engine; return engine;
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -30,11 +30,6 @@ import java.util.Map;
@SuppressWarnings({"serial", "rawtypes", "unchecked"}) @SuppressWarnings({"serial", "rawtypes", "unchecked"})
public class Kv extends HashMap { public class Kv extends HashMap {
@Deprecated
private static final String STATE_OK = "isOk";
@Deprecated
private static final String STATE_FAIL = "isFail";
public Kv() { public Kv() {
} }
@@ -46,73 +41,25 @@ public class Kv extends HashMap {
return new Kv(); return new Kv();
} }
@Deprecated
public static Kv ok() {
return new Kv().setOk();
}
@Deprecated
public static Kv ok(Object key, Object value) {
return ok().set(key, value);
}
@Deprecated
public static Kv fail() {
return new Kv().setFail();
}
@Deprecated
public static Kv fail(Object key, Object value) {
return fail().set(key, value);
}
@Deprecated
public Kv setOk() {
super.put(STATE_OK, Boolean.TRUE);
super.put(STATE_FAIL, Boolean.FALSE);
return this;
}
@Deprecated
public Kv setFail() {
super.put(STATE_FAIL, Boolean.TRUE);
super.put(STATE_OK, Boolean.FALSE);
return this;
}
@Deprecated
public boolean isOk() {
Boolean isOk = (Boolean)get(STATE_OK);
if (isOk != null) {
return isOk;
}
Boolean isFail = (Boolean)get(STATE_FAIL);
if (isFail != null) {
return !isFail;
}
throw new IllegalStateException("调用 isOk() 之前,必须先调用 ok()、fail() 或者 setOk()、setFail() 方法");
}
@Deprecated
public boolean isFail() {
Boolean isFail = (Boolean)get(STATE_FAIL);
if (isFail != null) {
return isFail;
}
Boolean isOk = (Boolean)get(STATE_OK);
if (isOk != null) {
return !isOk;
}
throw new IllegalStateException("调用 isFail() 之前,必须先调用 ok()、fail() 或者 setOk()、setFail() 方法");
}
public Kv set(Object key, Object value) { public Kv set(Object key, Object value) {
super.put(key, value); super.put(key, value);
return this; return this;
} }
public Kv setIfNotBlank(Object key, String value) {
if (StrKit.notBlank(value)) {
set(key, value);
}
return this;
}
public Kv setIfNotNull(Object key, Object value) {
if (value != null) {
set(key, value);
}
return this;
}
public Kv set(Map map) { public Kv set(Map map) {
super.putAll(map); super.putAll(map);
return this; return this;

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package com.jfinal.kit; package com.jfinal.kit;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/** /**
* 反射工具类 * 反射工具类
*/ */
@@ -24,11 +27,50 @@ public class ReflectKit {
public static Object newInstance(Class<?> clazz) { public static Object newInstance(Class<?> clazz) {
try { try {
return clazz.newInstance(); return clazz.newInstance();
} catch (Exception e) { } catch (ReflectiveOperationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public static String getMethodSignature(Method method) {
StringBuilder ret = new StringBuilder()
.append(method.getDeclaringClass().getName())
.append(".")
.append(method.getName())
.append("(");
int index = 0;
Parameter[] paras = method.getParameters();
for (Parameter p : paras) {
if (index++ > 0) {
ret.append(", ");
}
ret.append(p.getParameterizedType().getTypeName());
}
return ret.append(")").toString();
}
/*
public static String getMethodSignature(Method method) {
StringBuilder ret = new StringBuilder()
.append(method.getDeclaringClass().getName())
.append(".")
.append(method.getName())
.append("(");
int index = 0;
java.lang.reflect.Type[] paraTypes = method.getGenericParameterTypes();
for (java.lang.reflect.Type type : paraTypes) {
if (index++ > 0) {
ret.append(", ");
}
ret.append(type.getTypeName());
}
return ret.append(")").toString();
}*/
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -0,0 +1,92 @@
/**
* 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.kit;
import java.util.HashMap;
import java.util.Map;
/**
* SyncWriteMap 同步写 HashMap
* 创建原因是 HashMap扩容时遇到并发修改可能造成 100% CPU 占用
*
* SyncWriteMap 拥有 HashMap 的性能,但不保障并发访问的线程安全
* 只用于读多写少且不用保障线程安全的场景
*
* 例如 MethodKit 中用于缓存 MethodInfo 的 cache被写入的数据
* 不用保障是单例,读取之后会做 null 值判断
*
* ActionMapping 中的 HashMap 是系统启动时在独立线程内初始化的,
* 不存在并发写,只存在并发读的情况,所以仍然可以使用 HashMap
*/
public class SyncWriteMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = -7287230891751869148L;
public SyncWriteMap() {
}
public SyncWriteMap(int initialCapacity) {
super(initialCapacity);
}
public SyncWriteMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public SyncWriteMap(Map<? extends K, ? extends V> m) {
super(m);
}
@Override
public V put(K key, V value) {
synchronized (this) {
return super.put(key, value);
}
}
@Override
public V putIfAbsent(K key, V value) {
synchronized (this) {
return super.putIfAbsent(key, value);
}
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
synchronized (this) {
super.putAll(m);
}
}
@Override
public V remove(Object key) {
synchronized (this) {
return super.remove(key);
}
}
@Override
public void clear() {
synchronized (this) {
super.clear();
}
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,10 +21,17 @@ import java.util.HashMap;
import java.util.Map; 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.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.io.EncoderFactory;
import com.jfinal.template.source.ClassPathSourceFactory;
import com.jfinal.template.source.ISource; import com.jfinal.template.source.ISource;
import com.jfinal.template.source.ISourceFactory; import com.jfinal.template.source.ISourceFactory;
import com.jfinal.template.source.StringSource; import com.jfinal.template.source.StringSource;
import com.jfinal.template.stat.OutputDirectiveFactory;
import com.jfinal.template.stat.Parser; import com.jfinal.template.stat.Parser;
import com.jfinal.template.stat.ast.Stat; import com.jfinal.template.stat.ast.Stat;
@@ -40,7 +47,7 @@ public class Engine {
public static final String MAIN_ENGINE_NAME = "main"; public static final String MAIN_ENGINE_NAME = "main";
private static Engine MAIN_ENGINE; private static Engine MAIN_ENGINE;
private static Map<String, Engine> engineMap = new HashMap<String, Engine>(); private static Map<String, Engine> engineMap = new HashMap<String, Engine>(64, 0.5F);
// Create main engine // Create main engine
static { static {
@@ -53,7 +60,7 @@ public class Engine {
private EngineConfig config = new EngineConfig(); private EngineConfig config = new EngineConfig();
private ISourceFactory sourceFactory = config.getSourceFactory(); private ISourceFactory sourceFactory = config.getSourceFactory();
private Map<String, Template> templateCache = new HashMap<String, Template>(); private Map<String, Template> templateCache = new SyncWriteMap<String, Template>(2048, 0.5F);
/** /**
* Create engine without management of JFinal * Create engine without management of JFinal
@@ -123,7 +130,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) != '/') {
@@ -161,14 +168,6 @@ public class Engine {
/** /**
* Get template by string content and do not cache the template * Get template by string content and do not cache the template
*
* 重要StringSource 中的 key = HashKit.md5(content),也即 key
* 与 content 有紧密的对应关系,当 content 发生变化时 key 值也相应变化
* 因此,原先 key 所对应的 Template 缓存对象已无法被获取,当 getTemplateByString(String)
* 的 String 参数的数量不确定时会引发内存泄漏
*
* 当 getTemplateByString(String, boolean) 中的 String 参数的
* 数量可控并且确定时,才可对其使用缓存
*/ */
public Template getTemplateByString(String content) { public Template getTemplateByString(String content) {
return getTemplateByString(content, false); return getTemplateByString(content, false);
@@ -176,6 +175,15 @@ public class Engine {
/** /**
* Get template by string content * Get template by string content
*
* 重要StringSource 中的 cacheKey = HashKit.md5(content),也即 cacheKey
* 与 content 有紧密的对应关系,当 content 发生变化时 cacheKey 值也相应变化
* 因此,原先 cacheKey 所对应的 Template 缓存对象已无法被获取,当 getTemplateByString(String)
* 的 String 参数的数量不确定时会引发内存泄漏
*
* 当 getTemplateByString(String, boolean) 中的 String 参数的
* 数量可控并且确定时,才可对其使用缓存
*
* @param content 模板内容 * @param content 模板内容
* @param cache true 则缓存 Template否则不缓存 * @param cache true 则缓存 Template否则不缓存
*/ */
@@ -184,37 +192,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;
@@ -232,7 +240,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);
@@ -248,7 +256,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);
@@ -274,7 +282,7 @@ public class Engine {
/** /**
* Set output directive factory * Set output directive factory
*/ */
public Engine setOutputDirectiveFactory(IOutputDirectiveFactory outputDirectiveFactory) { public Engine setOutputDirectiveFactory(OutputDirectiveFactory outputDirectiveFactory) {
config.setOutputDirectiveFactory(outputDirectiveFactory); config.setOutputDirectiveFactory(outputDirectiveFactory);
return this; return this;
} }
@@ -332,7 +340,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);
@@ -356,10 +364,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);
} }
/** /**
@@ -426,6 +434,13 @@ public class Engine {
return this; return this;
} }
/**
* 设置为 ClassPathSourceFactory 的快捷方法
*/
public Engine setToClassPathSourceFactory() {
return setSourceFactory(new ClassPathSourceFactory());
}
public ISourceFactory getSourceFactory() { public ISourceFactory getSourceFactory() {
return sourceFactory; return sourceFactory;
} }
@@ -457,6 +472,17 @@ public class Engine {
return config.getEncoding(); return config.getEncoding();
} }
/**
* Enjoy 模板引擎对 UTF-8 的 encoding 做过性能优化,某些偏门字符在
* 被编码为 UTF-8 时会出现异常,此时可以通过继承扩展 EncoderFactory
* 来解决编码异常,具体用法参考:
* http://www.jfinal.com/feedback/5340
*/
public Engine setEncoderFactory(EncoderFactory encoderFactory) {
config.setEncoderFactory(encoderFactory);
return this;
}
public Engine setWriterBufferSize(int bufferSize) { public Engine setWriterBufferSize(int bufferSize) {
config.setWriterBufferSize(bufferSize); config.setWriterBufferSize(bufferSize);
return this; return this;
@@ -489,6 +515,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 表达式的值,其中 isXxx() 返回值
* 必须是 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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@ import com.jfinal.template.source.ISource;
import com.jfinal.template.source.ISourceFactory; import com.jfinal.template.source.ISourceFactory;
import com.jfinal.template.source.StringSource; import com.jfinal.template.source.StringSource;
import com.jfinal.template.stat.Location; import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.OutputDirectiveFactory;
import com.jfinal.template.stat.Parser; import com.jfinal.template.stat.Parser;
import com.jfinal.template.stat.ast.Define; import com.jfinal.template.stat.ast.Define;
import com.jfinal.template.stat.ast.Output; import com.jfinal.template.stat.ast.Output;
@@ -48,14 +49,14 @@ public class EngineConfig {
WriterBuffer writerBuffer = new WriterBuffer(); WriterBuffer writerBuffer = new WriterBuffer();
private Map<String, Define> sharedFunctionMap = new HashMap<String, Define>(); private Map<String, Define> sharedFunctionMap = createSharedFunctionMap(); // new HashMap<String, Define>(512, 0.25F);
private List<ISource> sharedFunctionSourceList = new ArrayList<ISource>(); // for devMode only private List<ISource> sharedFunctionSourceList = new ArrayList<ISource>(); // for devMode only
Map<String, Object> sharedObjectMap = null; Map<String, Object> sharedObjectMap = null;
private IOutputDirectiveFactory outputDirectiveFactory = OutputDirectiveFactory.me; private OutputDirectiveFactory outputDirectiveFactory = OutputDirectiveFactory.me;
private ISourceFactory sourceFactory = new FileSourceFactory(); private ISourceFactory sourceFactory = new FileSourceFactory();
private Map<String, Class<? extends Directive>> directiveMap = new HashMap<String, Class<? extends Directive>>(); private Map<String, Class<? extends Directive>> directiveMap = new HashMap<String, Class<? extends Directive>>(64, 0.5F);
private SharedMethodKit sharedMethodKit = new SharedMethodKit(); private SharedMethodKit sharedMethodKit = new SharedMethodKit();
private boolean devMode = false; private boolean devMode = false;
@@ -72,9 +73,9 @@ public class EngineConfig {
addDirective("string", StringDirective.class); addDirective("string", StringDirective.class);
addDirective("random", RandomDirective.class); addDirective("random", RandomDirective.class);
addDirective("number", NumberDirective.class); addDirective("number", NumberDirective.class);
addDirective("call", CallDirective.class);
// Add official shared method of Template Engine // Add official shared method of Template Engine
// addSharedMethod(new Json());
addSharedMethod(new SharedMethodLib()); addSharedMethod(new SharedMethodLib());
} }
@@ -82,6 +83,7 @@ public class EngineConfig {
* Add shared function with file * Add shared function with file
*/ */
public void addSharedFunction(String fileName) { public void addSharedFunction(String fileName) {
fileName = fileName.replace("\\", "/");
// FileSource fileSource = new FileSource(baseTemplatePath, fileName, encoding); // FileSource fileSource = new FileSource(baseTemplatePath, fileName, encoding);
ISource source = sourceFactory.getSource(baseTemplatePath, fileName, encoding); ISource source = sourceFactory.getSource(baseTemplatePath, fileName, encoding);
doAddSharedFunction(source, fileName); doAddSharedFunction(source, fileName);
@@ -179,7 +181,7 @@ public class EngineConfig {
* 开发者可直接使用模板注释功能将不需要的 function 直接注释掉 * 开发者可直接使用模板注释功能将不需要的 function 直接注释掉
*/ */
private synchronized void reloadSharedFunctionSourceList() { private synchronized void reloadSharedFunctionSourceList() {
Map<String, Define> newMap = new HashMap<String, Define>(); Map<String, Define> newMap = createSharedFunctionMap();
for (int i = 0, size = sharedFunctionSourceList.size(); i < size; i++) { for (int i = 0, size = sharedFunctionSourceList.size(); i < size; i++) {
ISource source = sharedFunctionSourceList.get(i); ISource source = sharedFunctionSourceList.get(i);
String fileName = source instanceof FileSource ? ((FileSource)source).getFileName() : null; String fileName = source instanceof FileSource ? ((FileSource)source).getFileName() : null;
@@ -194,9 +196,13 @@ public class EngineConfig {
this.sharedFunctionMap = newMap; this.sharedFunctionMap = newMap;
} }
private Map<String, Define> createSharedFunctionMap() {
return new HashMap<String, Define>(512, 0.25F);
}
public synchronized void addSharedObject(String name, Object object) { public synchronized void addSharedObject(String name, Object object) {
if (sharedObjectMap == null) { if (sharedObjectMap == null) {
sharedObjectMap = new HashMap<String, Object>(); sharedObjectMap = new HashMap<String, Object>(64, 0.25F);
} else if (sharedObjectMap.containsKey(name)) { } else if (sharedObjectMap.containsKey(name)) {
throw new IllegalArgumentException("Shared object already exists: " + name); throw new IllegalArgumentException("Shared object already exists: " + name);
} }
@@ -210,7 +216,7 @@ public class EngineConfig {
/** /**
* Set output directive factory * Set output directive factory
*/ */
public void setOutputDirectiveFactory(IOutputDirectiveFactory outputDirectiveFactory) { public void setOutputDirectiveFactory(OutputDirectiveFactory outputDirectiveFactory) {
if (outputDirectiveFactory == null) { if (outputDirectiveFactory == null) {
throw new IllegalArgumentException("outputDirectiveFactory can not be null"); throw new IllegalArgumentException("outputDirectiveFactory can not be null");
} }
@@ -256,8 +262,9 @@ public class EngineConfig {
throw new IllegalArgumentException("baseTemplatePath can not be blank"); throw new IllegalArgumentException("baseTemplatePath can not be blank");
} }
baseTemplatePath = baseTemplatePath.trim(); baseTemplatePath = baseTemplatePath.trim();
baseTemplatePath = baseTemplatePath.replace("\\", "/");
if (baseTemplatePath.length() > 1) { if (baseTemplatePath.length() > 1) {
if (baseTemplatePath.endsWith("/") || baseTemplatePath.endsWith("\\")) { if (baseTemplatePath.endsWith("/")) {
baseTemplatePath = baseTemplatePath.substring(0, baseTemplatePath.length() - 1); baseTemplatePath = baseTemplatePath.substring(0, baseTemplatePath.length() - 1);
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ import com.jfinal.template.stat.ast.Define;
public class Env { public class Env {
protected EngineConfig engineConfig; protected EngineConfig engineConfig;
protected Map<String, Define> functionMap = new HashMap<String, Define>(); protected Map<String, Define> functionMap = new HashMap<String, Define>(16, 0.5F);
// 代替 Template 持有该属性,便于在 #include 指令中调用 Env.addSource() // 代替 Template 持有该属性,便于在 #include 指令中调用 Env.addSource()
protected List<ISource> sourceList = null; protected List<ISource> sourceList = null;

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package com.jfinal.template; package com.jfinal.template;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Writer; import java.io.Writer;
import java.util.Map; import java.util.Map;
@@ -108,6 +111,26 @@ public class Template {
return fsw.getBuffer(); return fsw.getBuffer();
} }
/**
* 渲染到 File 中去
* 适用于代码生成器类似应用场景
*/
public void render(Map<?, ?> data, File file) {
try (FileOutputStream fos = new FileOutputStream(file)) {
render(data, fos);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 渲染到 String fileName 参数所指定的文件中去
* 适用于代码生成器类似应用场景
*/
public void render(Map<?, ?> data, String fileName) {
render(data, new File(fileName));
}
public boolean isModified() { public boolean isModified() {
return env.isSourceListModified(); return env.isSourceListModified();
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -28,8 +28,8 @@ public class TemplateException extends RuntimeException {
super(loc != null ? msg + loc : msg); super(loc != null ? msg + loc : msg);
} }
public TemplateException(String msg, Location loc, Throwable t) { public TemplateException(String msg, Location loc, Throwable cause) {
super(loc != null ? msg + loc : msg, t); super(loc != null ? msg + loc : msg, cause);
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,28 +21,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import com.jfinal.template.EngineConfig; import com.jfinal.template.EngineConfig;
import com.jfinal.template.expr.Sym; import com.jfinal.template.expr.Sym;
import com.jfinal.template.expr.ast.Arith; import com.jfinal.template.expr.ast.*;
import com.jfinal.template.expr.ast.Array;
import com.jfinal.template.expr.ast.Assign;
import com.jfinal.template.expr.ast.Compare;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.expr.ast.Field;
import com.jfinal.template.expr.ast.ForCtrl;
import com.jfinal.template.expr.ast.Id;
import com.jfinal.template.expr.ast.IncDec;
import com.jfinal.template.expr.ast.Index;
import com.jfinal.template.expr.ast.Logic;
import com.jfinal.template.expr.ast.Map;
import com.jfinal.template.expr.ast.Method;
import com.jfinal.template.expr.ast.NullSafe;
import com.jfinal.template.expr.ast.RangeArray;
import com.jfinal.template.expr.ast.SharedMethod;
import com.jfinal.template.expr.ast.StaticField;
import com.jfinal.template.expr.ast.StaticMethod;
import com.jfinal.template.expr.ast.Ternary;
import com.jfinal.template.expr.ast.Unary;
import com.jfinal.template.stat.Location; import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ParaToken; import com.jfinal.template.stat.ParaToken;
import com.jfinal.template.stat.ParseException; import com.jfinal.template.stat.ParseException;
@@ -292,7 +271,7 @@ public class ExprParser {
case ADD: case ADD:
case SUB: case SUB:
move(); move();
return new Unary(tok.sym, unary(), location); return new Unary(tok.sym, unary(), location).toConstIfPossible();
case INC: case INC:
case DEC: case DEC:
move(); move();
@@ -468,21 +447,26 @@ public class ExprParser {
} }
/** /**
* mapEntry : (ID | STR) ':' expr * mapEntry : (ID | STR | INT | LONG | FLOAT | DOUBLE | TRUE | FALSE | NULL) ':' expr
* 设计目标为 map 定义与初始化,所以 ID 仅当成 STR 不进行求值
*/ */
void buildMapEntry(LinkedHashMap<Object, Expr> map) { void buildMapEntry(LinkedHashMap<Object, Expr> map) {
Tok tok = peek(); Expr keyExpr = expr();
if (tok.sym == Sym.ID || tok.sym == Sym.STR) { Object key;
move(); if (keyExpr instanceof Id) {
match(Sym.COLON); key = ((Id)keyExpr).getId();
Expr value = expr(); } else if (keyExpr instanceof Const) {
if (value == null) { key = ((Const)keyExpr).getValue();
throw new ParseException("Expression error: the value on the right side of map entry can not be blank", location); } else {
} throw new ParseException("Expression error: the value of map key must be identifier, String, Boolean, null or Number", location);
map.put(tok.value(), value);
return ;
} }
throw new ParseException("Expression error: the value of map key must be identifier or String", location);
match(Sym.COLON);
Expr value = expr();
if (value == null) {
throw new ParseException("Expression error: the value on the right side of map entry can not be blank", location);
}
map.put(key, value);
} }
/** /**
@@ -528,12 +512,14 @@ public class ExprParser {
move(); move();
return new Id(tok.value()); return new Id(tok.value());
case STR: case STR:
move();
return new Const(tok.sym, tok.value());
case INT: case INT:
case LONG: case LONG:
case FLOAT: case FLOAT:
case DOUBLE: case DOUBLE:
move(); move();
return new Const(tok.sym, tok.value()); return new Const(tok.sym, ((NumTok)tok).getNumberValue());
case TRUE: case TRUE:
move(); move();
return Const.TRUE; return Const.TRUE;

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@ import com.jfinal.template.stat.ParseException;
*/ */
public class NumTok extends Tok { public class NumTok extends Tok {
private Object value; private Number value;
NumTok(Sym sym, String s, int radix, boolean isScientificNotation, Location location) { NumTok(Sym sym, String s, int radix, boolean isScientificNotation, Location location) {
super(sym, location.getRow()); super(sym, location.getRow());
@@ -101,3 +101,8 @@ public class NumTok extends Tok {
return sym.value() + " : " + value; return sym.value() + " : " + value;
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -134,11 +134,12 @@ public class Compare extends Expr {
} }
if (leftValue instanceof Comparable && if (leftValue instanceof Comparable &&
rightValue != null &&
leftValue.getClass() == rightValue.getClass()) { leftValue.getClass() == rightValue.getClass()) {
return ((Comparable)leftValue).compareTo((Comparable)rightValue) > 0; return ((Comparable)leftValue).compareTo((Comparable)rightValue) > 0;
} }
return checkType(leftValue, rightValue); return checkComparisonValue(leftValue, rightValue);
} }
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
@@ -166,11 +167,12 @@ public class Compare extends Expr {
} }
if (leftValue instanceof Comparable && if (leftValue instanceof Comparable &&
rightValue != null &&
leftValue.getClass() == rightValue.getClass()) { leftValue.getClass() == rightValue.getClass()) {
return ((Comparable)leftValue).compareTo((Comparable)rightValue) >= 0; return ((Comparable)leftValue).compareTo((Comparable)rightValue) >= 0;
} }
return checkType(leftValue, rightValue); return checkComparisonValue(leftValue, rightValue);
} }
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
@@ -198,11 +200,12 @@ public class Compare extends Expr {
} }
if (leftValue instanceof Comparable && if (leftValue instanceof Comparable &&
rightValue != null &&
leftValue.getClass() == rightValue.getClass()) { leftValue.getClass() == rightValue.getClass()) {
return ((Comparable)leftValue).compareTo((Comparable)rightValue) < 0; return ((Comparable)leftValue).compareTo((Comparable)rightValue) < 0;
} }
return checkType(leftValue, rightValue); return checkComparisonValue(leftValue, rightValue);
} }
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
@@ -230,11 +233,12 @@ public class Compare extends Expr {
} }
if (leftValue instanceof Comparable && if (leftValue instanceof Comparable &&
rightValue != null &&
leftValue.getClass() == rightValue.getClass()) { leftValue.getClass() == rightValue.getClass()) {
return ((Comparable)leftValue).compareTo((Comparable)rightValue) <= 0; return ((Comparable)leftValue).compareTo((Comparable)rightValue) <= 0;
} }
return checkType(leftValue, rightValue); return checkComparisonValue(leftValue, rightValue);
} }
private int getMaxType(Number obj1, Number obj2) { private int getMaxType(Number obj1, Number obj2) {
@@ -270,7 +274,7 @@ public class Compare extends Expr {
return ret; return ret;
} }
private Boolean checkType(Object leftValue, Object rightValue) { private Boolean checkComparisonValue(Object leftValue, Object rightValue) {
if (leftValue == null) { if (leftValue == null) {
throw new TemplateException("The operation target on the left side of \"" + op.value() + "\" can not be null", location); throw new TemplateException("The operation target on the left side of \"" + op.value() + "\" can not be null", location);
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -20,54 +20,25 @@ import com.jfinal.template.expr.Sym;
import com.jfinal.template.stat.Scope; import com.jfinal.template.stat.Scope;
/** /**
* STR INT LONG FLOAT DOUBLE true false null * STR INT LONG FLOAT DOUBLE TRUE FALSE NULL
*/ */
public class Const extends Expr { public class Const extends Expr {
public static final Const TRUE = new Const(Boolean.TRUE, Sym.TRUE); public static final Const TRUE = new Const(Sym.TRUE, Boolean.TRUE);
public static final Const FALSE = new Const(Boolean.FALSE, Sym.FALSE); public static final Const FALSE = new Const(Sym.FALSE, Boolean.FALSE);
public static final Const NULL = new Const(null, Sym.NULL); public static final Const NULL = new Const(Sym.NULL, null);
private Sym type; private final Sym type;
private Object value; private final Object value;
/** /**
* 构造 TRUE FALSE NULL 常量,无需对 value 进行转换 * INT LONG FLOAT DOUBLE 常量已在 NumTok 中转换成了确切的类型,无需再次转换
*/ */
private Const(Object value, Sym type) { public Const(Sym type, Object value) {
this.type = type; this.type = type;
this.value = value; this.value = value;
} }
public Const(Sym type, String value) {
this.type = type;
this.value = typeConvert(type, value);
}
private Object typeConvert(Sym type, String value) {
switch (type) {
case STR:
return value;
case INT:
return Integer.parseInt(value);
case LONG:
return Long.parseLong(value);
case FLOAT:
return Float.parseFloat(value);
case DOUBLE:
return Double.parseDouble(value);
/*
case TRUE:
case FALSE:
return Boolean.parseBoolean(value);
case NULL:
return null;
*/
default:
throw new RuntimeException("never happend");
}
}
public Object eval(Scope scope) { public Object eval(Scope scope) {
return value; return value;
} }
@@ -108,6 +79,10 @@ public class Const extends Expr {
return type == Sym.DOUBLE; return type == Sym.DOUBLE;
} }
public boolean isNumber() {
return value instanceof Number;
}
public Object getValue() { public Object getValue() {
return value; return value;
} }
@@ -136,6 +111,10 @@ public class Const extends Expr {
return (Double)value; return (Double)value;
} }
public Number getNumber() {
return (Number)value;
}
public String toString() { public String toString() {
return value != null ? value.toString() : "null"; return value != null ? value.toString() : "null";
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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;
@@ -31,10 +28,10 @@ import com.jfinal.template.stat.Scope;
* *
* field 表达式取值优先次序,以 user.name 为例 * field 表达式取值优先次序,以 user.name 为例
* 1假如 user.getName() 存在,则优先调用 * 1假如 user.getName() 存在,则优先调用
* 2假如 user 为 Model 子类,则调用 user.get("name") * 2假如 user 具有 public name 属性,则取 user.name 属性值
* 3假如 user 为 Record,则调用 user.get("name") * 3假如 user 为 Model 子类,则调用 user.get("name")
* 4假如 user 为 Map,则调用 user.get("name") * 4假如 user 为 Record,则调用 user.get("name")
* 5假如 user 具有 public name 属性,则取 user.name 属性值 * 5假如 user 为 Map则调用 user.get("name")
*/ */
public class Field extends Expr { public class Field extends Expr {
@@ -68,59 +65,34 @@ 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 | 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("Field not found: \"" + id + "." + fieldName + "\" and 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);
} }
throw new TemplateException("Field not found: \"" + fieldName + "\" and 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 用于封装 targetClass、fieldName 这两部分的 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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,38 +16,129 @@
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.template.expr.ast.FieldGetters.*;
/** /**
* FieldKit * FieldKit
*/ */
public class FieldKit { public class FieldKit {
private static final HashMap<Long, Object> fieldCache = new HashMap<Long, Object>(); 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 RealFieldGetter(null));
// ret.addLast(new ModelFieldGetter());
// ret.addLast(new RecordFieldGetter());
ret.addLast(new MapFieldGetter());
// 挪到第二的位置addSharedObject(..., modelObj) 用法可以获取到 model 中的 public 属性
// 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()]);
}
public static void clearCache() {
fieldGetterCache.clear();
} }
} }
@@ -55,3 +146,6 @@ public class FieldKit {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@ import com.jfinal.template.stat.Scope;
*/ */
public class Id extends Expr { public class Id extends Expr {
private String id; private final String id;
public Id(String id) { public Id(String id) {
this.id = id; this.id = id;

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,8 +43,8 @@ public class Index extends Expr {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public Object eval(Scope scope) { public Object eval(Scope scope) {
Object array = expr.eval(scope); Object target = expr.eval(scope);
if (array == null) { if (target == null) {
if (scope.getCtrl().isNullSafe()) { if (scope.getCtrl().isNullSafe()) {
return null; return null;
} }
@@ -56,25 +56,30 @@ public class Index extends Expr {
if (scope.getCtrl().isNullSafe()) { if (scope.getCtrl().isNullSafe()) {
return null; return null;
} }
throw new TemplateException("The index of list/array and the key of map can not be null", location);
}
if (array instanceof List) { if (target instanceof java.util.Map) {
if (idx instanceof Integer) { // Map 的 key 可以是 null不能抛异常
return ((List<?>)array).get((Integer)idx); } else {
throw new TemplateException("The index of list and array can not be null", location);
} }
throw new TemplateException("The index of list can only be integer", location);
} }
if (array instanceof java.util.Map) { if (target instanceof List) {
return ((java.util.Map)array).get(idx);
}
if (array.getClass().isArray()) {
if (idx instanceof Integer) { if (idx instanceof Integer) {
return java.lang.reflect.Array.get(array, (Integer)idx); return ((List<?>)target).get((Integer)idx);
} }
throw new TemplateException("The index of array can only be integer", location); throw new TemplateException("The index of list must be integer", location);
}
if (target instanceof java.util.Map) {
return ((java.util.Map)target).get(idx);
}
if (target.getClass().isArray()) {
if (idx instanceof Integer) {
return java.lang.reflect.Array.get(target, (Integer)idx);
}
throw new TemplateException("The index of array must be integer", location);
} }
throw new TemplateException("Only the list array and map is supported by index access", location); throw new TemplateException("Only the list array and map is supported by index access", location);

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -24,8 +24,10 @@ import com.jfinal.template.stat.Scope;
* Map * Map
* *
* 1定义 map 常量 * 1定义 map 常量
* {k1:123, k2:"abc", 'k3':true, "k4":[1,2,3], k5:1+2} * {k1:123, k2:"abc", 'k3':true, "k4":[1,2,3], k5:1+2, 1:12, true:"Y", null:"abc"}
* 如上所示map定义的 key 可以为 String 或者 id 标识符,而右侧的 value 可以是任意的常量与表达式 * 如上所示map定义的 key 可以为 id 标识符或者 String、Integer、Long、Boolean、null
* 等常量值 (详见 ExprParser.buildMapEntry(...) 方法)
* 右侧的 value 可以是任意的常量与表达式
* *
* 2取值 * 2取值
* 先将 Map 常量赋值给某个变量: #set(map = {...}) * 先将 Map 常量赋值给某个变量: #set(map = {...})
@@ -35,6 +37,10 @@ import com.jfinal.template.stat.Scope;
* map.get("k1") * map.get("k1")
* map.k1 * map.k1
* *
* 3不通过中间变量取值
* {1:'自买', 2:'跟买'}.get(1)
* {1:'自买', 2:'跟买'}[2]
*
* 如上所示,当以下标方式取值时,下标参数可以是 string 与 expr而 expr 求值以后的值必须也为 string类型 * 如上所示,当以下标方式取值时,下标参数可以是 string 与 expr而 expr 求值以后的值必须也为 string类型
* 当用 map.k1 这类 field 字段取值形式时,则是使用 id 标识符,而不是 string 形参数 * 当用 map.k1 这类 field 字段取值形式时,则是使用 id 标识符,而不是 string 形参数
* *

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -24,6 +24,17 @@ import com.jfinal.template.stat.Scope;
/** /**
* Method : expr '.' ID '(' exprList? ')' * Method : expr '.' ID '(' exprList? ')'
*
* 每次通过 MethodKit.getMethod(...) 取 MethodInfo 而不是用属性持有其对象
* 是为了支持 target 对象的动态类型MethodInfo 中的 Method 被调用 15 次以后
* 会被 JDK 动态生成 GeneratedAccessorXXX 字节码,性能不是问题
* 唯一的性能损耗是从 HashMap 中获取 MethodInfo 对象,可以忽略不计
*
* 如果在未来通过结合 #dynamic(boolean) 指令来优化,需要在 Ctrl 中引入一个
* boolean dynamic = false 变量,而不能在 Env、Scope 引入该变量
*
* 还需要引入一个 NullMethodInfo 以及 notNull() 方法,此优化复杂度提高不少,
* 暂时不做此优化
*/ */
public class Method extends Expr { public class Method extends Expr {
@@ -65,28 +76,24 @@ 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("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 | ParseException e) {
return methodInfo.invoke(target, argValues); 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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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;
} }
/** /**
@@ -87,6 +90,9 @@ public abstract class MethodKeyBuilder {
if (type != null) { if (type != null) {
hash ^= type.getName().hashCode(); hash ^= type.getName().hashCode();
hash *= HashKit.FNV_PRIME_64; hash *= HashKit.FNV_PRIME_64;
} else {
hash ^= "null".hashCode();
hash *= HashKit.FNV_PRIME_64;
} }
} }
} }
@@ -110,6 +116,8 @@ public abstract class MethodKeyBuilder {
Class<?> type = argTypes[i]; Class<?> type = argTypes[i];
if (type != null) { if (type != null) {
hash = fnv1a64(hash, type.getName()); hash = fnv1a64(hash, type.getName());
} else {
hash = fnv1a64(hash, "null");
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.jfinal.kit.ReflectKit; import com.jfinal.kit.ReflectKit;
import com.jfinal.kit.SyncWriteMap;
import com.jfinal.template.ext.extensionmethod.ByteExt; import com.jfinal.template.ext.extensionmethod.ByteExt;
import com.jfinal.template.ext.extensionmethod.DoubleExt; import com.jfinal.template.ext.extensionmethod.DoubleExt;
import com.jfinal.template.ext.extensionmethod.FloatExt; import com.jfinal.template.ext.extensionmethod.FloatExt;
@@ -37,17 +38,18 @@ import com.jfinal.template.ext.extensionmethod.StringExt;
public class MethodKit { public class MethodKit {
private static final Class<?>[] NULL_ARG_TYPES = new Class<?>[0]; private static final Class<?>[] NULL_ARG_TYPES = new Class<?>[0];
private static final Set<String> forbiddenMethods = new HashSet<String>(); private static final Set<String> forbiddenMethods = new HashSet<String>(64);
private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>(); private static final Set<Class<?>> forbiddenClasses = new HashSet<Class<?>>(64);
private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>(); private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>(64);
private static final HashMap<Long, Object> methodCache = new HashMap<Long, Object>(); private static final SyncWriteMap<Long, Object> methodCache = new SyncWriteMap<Long, Object>(2048, 0.25F);
// 初始化在模板中调用 method 时所在的被禁止使用类 // 初始化在模板中调用 method 时所在的被禁止使用类
static { static {
Class<?>[] cs = { Class<?>[] cs = {
System.class, Runtime.class, Thread.class, Class.class, ClassLoader.class, File.class, System.class, Runtime.class, Thread.class, Class.class, ClassLoader.class, File.class,
Compiler.class, InheritableThreadLocal.class, Package.class, Process.class, Compiler.class, InheritableThreadLocal.class, Package.class, Process.class,
RuntimePermission.class, SecurityManager.class, ThreadGroup.class, ThreadLocal.class RuntimePermission.class, SecurityManager.class, ThreadGroup.class, ThreadLocal.class,
java.lang.reflect.Method.class
}; };
for (Class<?> c : cs) { for (Class<?> c : cs) {
forbiddenClasses.add(c); forbiddenClasses.add(c);
@@ -58,7 +60,7 @@ public class MethodKit {
static { static {
String[] ms = { String[] ms = {
"getClass", "getDeclaringClass", "forName", "newInstance", "getClassLoader", "getClass", "getDeclaringClass", "forName", "newInstance", "getClassLoader",
"getMethod", "getMethods", "getField", "getFields", "invoke", // "getMethod", "getMethods", // "getField", "getFields",
"notify", "notifyAll", "wait", "notify", "notifyAll", "wait",
"load", "exit", "loadLibrary", "halt", "load", "exit", "loadLibrary", "halt",
"stop", "suspend", "resume", "setDaemon", "setPriority", "stop", "suspend", "resume", "setDaemon", "setPriority",
@@ -93,6 +95,14 @@ public class MethodKit {
return forbiddenClasses.contains(clazz); return forbiddenClasses.contains(clazz);
} }
public static void addForbiddenClass(Class<?> clazz) {
forbiddenClasses.add(clazz);
}
public static void removeForbiddenClass(Class<?> clazz) {
forbiddenClasses.remove(clazz);
}
public static boolean isForbiddenMethod(String methodName) { public static boolean isForbiddenMethod(String methodName) {
return forbiddenMethods.contains(methodName); return forbiddenMethods.contains(methodName);
} }
@@ -101,6 +111,14 @@ public class MethodKit {
forbiddenMethods.add(methodName); forbiddenMethods.add(methodName);
} }
public static void removeForbiddenMethod(String methodName) {
forbiddenMethods.remove(methodName);
}
public static void clearCache() {
methodCache.clear();
}
public static MethodInfo getMethod(Class<?> targetClass, String methodName, Object[] argValues) { public static MethodInfo getMethod(Class<?> targetClass, String methodName, Object[] argValues) {
Class<?>[] argTypes = getArgTypes(argValues); Class<?>[] argTypes = getArgTypes(argValues);
Long key = getMethodKey(targetClass, methodName, argTypes); Long key = getMethodKey(targetClass, methodName, argTypes);
@@ -108,10 +126,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;
@@ -120,19 +138,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) {
@@ -279,7 +297,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);
} }
} }
} }
@@ -307,7 +325,7 @@ public class MethodKit {
} }
} }
private static final Map<Class<?>, Class<?>> primitiveToBoxedMap = new HashMap<Class<?>, Class<?>>(); private static final Map<Class<?>, Class<?>> primitiveToBoxedMap = new HashMap<Class<?>, Class<?>>(64);
// 初始化 primitive type 到 boxed type 的映射 // 初始化 primitive type 到 boxed type 的映射
static { static {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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,18 @@ 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 | 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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -22,11 +22,11 @@ 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;
import com.jfinal.kit.ReflectKit; import com.jfinal.kit.ReflectKit;
import com.jfinal.kit.SyncWriteMap;
/** /**
* SharedMethodKit * SharedMethodKit
@@ -44,7 +44,7 @@ public class SharedMethodKit {
} }
private final List<SharedMethodInfo> sharedMethodList = new ArrayList<SharedMethodInfo>(); private final List<SharedMethodInfo> sharedMethodList = new ArrayList<SharedMethodInfo>();
private final HashMap<Long, SharedMethodInfo> methodCache = new HashMap<Long, SharedMethodInfo>(); private final HashMap<Long, SharedMethodInfo> methodCache = new SyncWriteMap<Long, SharedMethodInfo>(512, 0.25F);
public SharedMethodInfo getSharedMethodInfo(String methodName, Object[] argValues) { public SharedMethodInfo getSharedMethodInfo(String methodName, Object[] argValues) {
Class<?>[] argTypes = MethodKit.getArgTypes(argValues); Class<?>[] argTypes = MethodKit.getArgTypes(argValues);
@@ -53,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;
} }
@@ -156,6 +156,9 @@ public class SharedMethodKit {
if (type != null) { if (type != null) {
hash ^= type.getName().hashCode(); hash ^= type.getName().hashCode();
hash *= HashKit.FNV_PRIME_64; hash *= HashKit.FNV_PRIME_64;
} else {
hash ^= "null".hashCode();
hash *= HashKit.FNV_PRIME_64;
} }
} }
} }
@@ -170,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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -57,23 +57,23 @@ 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 | 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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -83,6 +83,51 @@ public class Unary extends Expr {
throw new TemplateException("Unsupported operator: " + op.value(), location); throw new TemplateException("Unsupported operator: " + op.value(), location);
} }
} }
/**
* 如果可能的话,将 Unary 表达式转化成 Const 表达式,类似于 ExprParser.buildMapEntry() 需要这种转化来简化实现
* 除了可简化程序外,还起到一定的性能优化作用
*
* Number : +123 -456 +3.14 -0.12
* Boolean : !true !false
*
* 特别注意:
* Boolean 的支持并不需要,!true、!false 已在 ExprParser 中被 Logic 表达式接管,在此仅为逻辑上的完备性而添加
*/
public Expr toConstIfPossible() {
if (expr instanceof Const && (op == Sym.SUB || op == Sym.ADD || op == Sym.NOT)) {
} else {
return this;
}
Expr ret = this;
Const c = (Const)expr;
if (op == Sym.SUB) {
if (c.isInt()) {
ret = new Const(Sym.INT, -c.getInt());
} else if (c.isLong()) {
ret = new Const(Sym.LONG, -c.getLong());
} else if (c.isFloat()) {
ret = new Const(Sym.FLOAT, -c.getFloat());
} else if (c.isDouble()) {
ret = new Const(Sym.DOUBLE, -c.getDouble());
}
} else if (op == Sym.ADD) {
if (c.isNumber()) {
ret = c;
}
} else if (op == Sym.NOT) {
if (c.isBoolean()) {
ret = c.isTrue() ? Const.FALSE : Const.TRUE;
}
}
return ret;
}
public String toString() {
return op.toString() + expr.toString();
}
} }

View File

@@ -0,0 +1,109 @@
/**
* 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.ext.directive;
import java.util.ArrayList;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
import com.jfinal.template.stat.ast.Define;
/**
* CallDirective 动态调用模板函数
*
* 模板函数的名称与参数都可以动态指定,提升模板函数调用的灵活性
*
* 例如:
* #call(funcName, p1, p2, ..., pn)
* 其中 funcName为函数名p1、p2、pn 为被调用函数所使用的参数
*
*
* 如果希望模板函数不存在时忽略其调用,添加常量值 true 在第一个参数位置即可
* 例如:
* #call(true, funcName, p1, p2, ..., pn)
*
*
* TODO 后续优化看一下 ast.Call.java
*/
public class CallDirective extends Directive {
protected Expr funcNameExpr;
protected ExprList paraExpr;
protected boolean nullSafe = false; // 是否支持函数名不存在时跳过
public void setExprList(ExprList exprList) {
int len = exprList.length();
if (len == 0) {
throw new ParseException("模板函数名不能缺失", location);
}
int index = 0;
Expr expr = exprList.getExpr(index);
if (expr instanceof Const && ((Const)expr).isBoolean()) {
if (len == 1) {
throw new ParseException("模板函数名不能缺失", location);
}
nullSafe = ((Const)expr).getBoolean();
index++;
}
funcNameExpr = exprList.getExpr(index++);
ArrayList<Expr> list = new ArrayList<Expr>();
for (int i=index; i<len; i++) {
list.add(exprList.getExpr(i));
}
paraExpr = new ExprList(list);
}
public void exec(Env env, Scope scope, Writer writer) {
Object funcNameValue = funcNameExpr.eval(scope);
if (funcNameValue == null) {
if (nullSafe) {
return ;
}
throw new TemplateException("模板函数名为 null", location);
}
if (!(funcNameValue instanceof String)) {
throw new TemplateException("模板函数名必须是字符串", location);
}
Define func = env.getFunction(funcNameValue.toString());
if (func == null) {
if (nullSafe) {
return ;
}
throw new TemplateException("模板函数未找到 : " + funcNameValue, location);
}
func.call(env, scope, paraExpr, writer);
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -79,23 +79,25 @@ public class DateDirective extends Directive {
private void outputWithoutDatePattern(Env env, Scope scope, Writer writer) { private void outputWithoutDatePattern(Env env, Scope scope, Writer writer) {
Object value = valueExpr.eval(scope); Object value = valueExpr.eval(scope);
if (value != null) { if (value instanceof Date) {
write(writer, (Date)value, env.getEngineConfig().getDatePattern()); write(writer, (Date)value, env.getEngineConfig().getDatePattern());
} else if (value != null) {
throw new TemplateException("The first parameter date of #date directive must be Date type", location);
} }
} }
private void outputWithDatePattern(Env env, Scope scope, Writer writer) { private void outputWithDatePattern(Env env, Scope scope, Writer writer) {
Object value = valueExpr.eval(scope); Object value = valueExpr.eval(scope);
if (value == null) { if (value instanceof Date) {
return ; Object datePattern = this.datePatternExpr.eval(scope);
if (datePattern instanceof String) {
write(writer, (Date)value, (String)datePattern);
} else {
throw new TemplateException("The sencond parameter datePattern of #date directive must be String", location);
}
} else if (value != null) {
throw new TemplateException("The first parameter date of #date directive must be Date type", location);
} }
Object datePattern = this.datePatternExpr.eval(scope);
if ( !(datePattern instanceof String) ) {
throw new TemplateException("The sencond parameter datePattern of #date directive must be String", location);
}
write(writer, (Date)value, (String)datePattern);
} }
private void write(Writer writer, Date date, String datePattern) { private void write(Writer writer, Date date, String datePattern) {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -52,11 +52,12 @@ public class EscapeDirective extends Directive {
case '>': case '>':
ret.append("&gt;"); ret.append("&gt;");
break; break;
case '\"': case '"':
ret.append("&quot;"); ret.append("&quot;");
break; break;
case '\'': case '\'':
ret.append("&apos;"); // IE 不支持 &apos; 考虑 &#39; // ret.append("&apos;"); // IE 不支持 &apos; 考虑 &#39;
ret.append("&#39;");
break; break;
case '&': case '&':
ret.append("&amp;"); ret.append("&amp;");

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import com.jfinal.template.stat.Scope;
*/ */
public class NowDirective extends Directive { public class NowDirective extends Directive {
public void setExrpList(ExprList exprList) { public void setExprList(ExprList exprList) {
if (exprList.length() > 1) { if (exprList.length() > 1) {
throw new ParseException("#now directive support one parameter only", location); throw new ParseException("#now directive support one parameter only", location);
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,22 +16,29 @@
package com.jfinal.template.ext.directive; package com.jfinal.template.ext.directive;
import java.io.IOException;
import com.jfinal.template.Directive; import com.jfinal.template.Directive;
import com.jfinal.template.Env; import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.io.Writer; import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope; import com.jfinal.template.stat.Scope;
/** /**
* 输出随机数 * 输出 int 型随机数
*/ */
public class RandomDirective extends Directive { public class RandomDirective extends Directive {
private java.util.Random random = new java.util.Random(); private java.util.Random random = new java.util.Random();
public void exec(Env env, Scope scope, Writer writer) { public void exec(Env env, Scope scope, Writer writer) {
write(writer, String.valueOf(random.nextInt())); try {
writer.write(random.nextInt());
} catch (IOException e) {
throw new TemplateException(e.getMessage(), location, e);
}
} }
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,8 +16,8 @@
package com.jfinal.template.ext.directive; package com.jfinal.template.ext.directive;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.jfinal.kit.SyncWriteMap;
import com.jfinal.template.Directive; import com.jfinal.template.Directive;
import com.jfinal.template.EngineConfig; import com.jfinal.template.EngineConfig;
import com.jfinal.template.Env; import com.jfinal.template.Env;
@@ -60,7 +60,7 @@ import com.jfinal.template.stat.ast.StatList;
public class RenderDirective extends Directive { public class RenderDirective extends Directive {
private String parentFileName; private String parentFileName;
private Map<String, StatInfo> statInfoCache = new HashMap<String,StatInfo>(); private Map<String, SubStat> subStatCache = new SyncWriteMap<String, SubStat>(16, 0.5F);
public void setExprList(ExprList exprList) { public void setExprList(ExprList exprList) {
int len = exprList.length(); int len = exprList.length();
@@ -70,7 +70,7 @@ public class RenderDirective extends Directive {
if (len > 1) { if (len > 1) {
for (int i = 1; i < len; i++) { for (int i = 1; i < len; i++) {
if (!(exprList.getExpr(i) instanceof Assign)) { if (!(exprList.getExpr(i) instanceof Assign)) {
throw new ParseException("The " + i + "th parameter of #render directive must be an assignment expression", location); throw new ParseException("The " + (i + 1) + "th parameter of #render directive must be an assignment expression", location);
} }
} }
} }
@@ -108,62 +108,68 @@ public class RenderDirective extends Directive {
} }
String subFileName = Include.getSubFileName((String)value, parentFileName); String subFileName = Include.getSubFileName((String)value, parentFileName);
StatInfo statInfo = statInfoCache.get(subFileName); SubStat subStat = subStatCache.get(subFileName);
if (statInfo == null) { if (subStat == null) {
statInfo = parseStatInfo(env, subFileName); subStat = parseSubStat(env, subFileName);
statInfoCache.put(subFileName, statInfo); subStatCache.put(subFileName, subStat);
} else if (env.isDevMode()) { } else if (env.isDevMode()) {
// statInfo.env.isSourceListModified() 逻辑可以支持 #render 子模板中的 #include 过来的子模板在 devMode 下在修改后可被重加载 // subStat.env.isSourceListModified() 逻辑可以支持 #render 子模板中的 #include 过来的子模板在 devMode 下在修改后可被重加载
if (statInfo.source.isModified() || statInfo.env.isSourceListModified()) { if (subStat.source.isModified() || subStat.env.isSourceListModified()) {
statInfo = parseStatInfo(env, subFileName); subStat = parseSubStat(env, subFileName);
statInfoCache.put(subFileName, statInfo); subStatCache.put(subFileName, subStat);
} }
} }
statInfo.stat.exec(statInfo.env, scope, writer); subStat.exec(null, scope, writer); // subStat.stat.exec(subStat.env, scope, writer);
scope.getCtrl().setJumpNone(); scope.getCtrl().setJumpNone();
} }
private StatInfo parseStatInfo(Env env, String subFileName) { private SubStat parseSubStat(Env env, String subFileName) {
EngineConfig config = env.getEngineConfig(); EngineConfig config = env.getEngineConfig();
// FileSource fileSource = new FileSource(config.getBaseTemplatePath(), subFileName, config.getEncoding()); // FileSource subFileSource = new FileSource(config.getBaseTemplatePath(), subFileName, config.getEncoding());
ISource fileSource = config.getSourceFactory().getSource(config.getBaseTemplatePath(), subFileName, config.getEncoding()); ISource subFileSource = config.getSourceFactory().getSource(config.getBaseTemplatePath(), subFileName, config.getEncoding());
try { try {
EnvSub envSub = new EnvSub(env); SubEnv subEnv = new SubEnv(env);
StatList statList = new Parser(envSub, fileSource.getContent(), subFileName).parse(); StatList subStatList = new Parser(subEnv, subFileSource.getContent(), subFileName).parse();
return new StatInfo(envSub, statList.getActualStat(), fileSource); return new SubStat(subEnv, subStatList.getActualStat(), subFileSource);
} catch (Exception e) { } catch (Exception e) {
throw new ParseException(e.getMessage(), location, e); throw new ParseException(e.getMessage(), location, e);
} }
} }
private static class StatInfo { public static class SubStat extends Stat {
EnvSub env; public SubEnv env;
Stat stat; public Stat stat;
ISource source; public ISource source;
StatInfo(EnvSub 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);
}
} }
/** /**
* EnvSub 用于将子模板与父模板中的模板函数隔离开来, * SubEnv 用于将子模板与父模板中的模板函数隔离开来,
* 否则在子模板被修改并被重新解析时会再次添加子模板中的 * 否则在子模板被修改并被重新解析时会再次添加子模板中的
* 模板函数,从而抛出异常 * 模板函数,从而抛出异常
* *
* EnvSub 也可以使子模板中定义的模板函数不与上层产生冲突, * SubEnv 也可以使子模板中定义的模板函数不与上层产生冲突,
* 有利于动态型模板渲染的模块化 * 有利于动态型模板渲染的模块化
* *
* 注意: #render 子模板中定义的模板函数无法被上层调用 * 注意: #render 子模板中定义的模板函数无法在父模板中调用
*/ */
private static class EnvSub extends Env { public static class SubEnv extends Env {
Env parentEnv; public Env parentEnv;
public EnvSub(Env parentEnv) { public SubEnv(Env parentEnv) {
super(parentEnv.getEngineConfig()); super(parentEnv.getEngineConfig());
this.parentEnv = parentEnv; this.parentEnv = parentEnv;
} }
@@ -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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,6 +43,14 @@ public class ByteExt {
public Double toDouble(Byte self) { public Double toDouble(Byte self) {
return self.doubleValue(); return self.doubleValue();
} }
public Short toShort(Byte self) {
return self.shortValue();
}
public Byte toByte(Byte self) {
return self;
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,6 +43,14 @@ public class DoubleExt {
public Double toDouble(Double self) { public Double toDouble(Double self) {
return self; return self;
} }
public Short toShort(Double self) {
return self.shortValue();
}
public Byte toByte(Double self) {
return self.byteValue();
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,6 +43,14 @@ public class FloatExt {
public Double toDouble(Float self) { public Double toDouble(Float self) {
return self.doubleValue(); return self.doubleValue();
} }
public Short toShort(Float self) {
return self.shortValue();
}
public Byte toByte(Float self) {
return self.byteValue();
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -63,6 +63,14 @@ public class IntegerExt {
public Double toDouble(Integer self) { public Double toDouble(Integer self) {
return self.doubleValue(); return self.doubleValue();
} }
public Short toShort(Integer self) {
return self.shortValue();
}
public Byte toByte(Integer self) {
return self.byteValue();
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,6 +43,14 @@ public class LongExt {
public Double toDouble(Long self) { public Double toDouble(Long self) {
return self.doubleValue(); return self.doubleValue();
} }
public Short toShort(Long self) {
return self.shortValue();
}
public Byte toByte(Long self) {
return self.byteValue();
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,6 +43,14 @@ public class ShortExt {
public Double toDouble(Short self) { public Double toDouble(Short self) {
return self.doubleValue(); return self.doubleValue();
} }
public Short toShort(Short self) {
return self;
}
public Byte toByte(Short self) {
return self.byteValue();
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -79,6 +79,14 @@ public class StringExt {
public Double toDouble(String self) { public Double toDouble(String self) {
return StrKit.isBlank(self) ? null : Double.parseDouble(self); return StrKit.isBlank(self) ? null : Double.parseDouble(self);
} }
public Short toShort(String self) {
return StrKit.isBlank(self) ? null : Short.parseShort(self);
}
public Byte toByte(String self) {
return StrKit.isBlank(self) ? null : Byte.parseByte(self);
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package com.jfinal.template.ext.spring; package com.jfinal.template.ext.spring;
import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
@@ -56,104 +57,120 @@ public class JFinalView extends AbstractTemplateView {
} }
} }
OutputStream os = response.getOutputStream(); try {
JFinalViewResolver.engine.getTemplate(getUrl()).render(model, os); OutputStream os = response.getOutputStream();
} JFinalViewResolver.engine.getTemplate(getUrl()).render(model, os);
} } catch (Exception e) { // 捕获 ByteWriter.close() 抛出的 RuntimeException
Throwable cause = e.getCause();
if (cause instanceof IOException) { // ClientAbortException、EofException 直接或间接继承自 IOException
String name = cause.getClass().getSimpleName();
if ("ClientAbortException".equals(name) || "EofException".equals(name)) {
return ;
}
}
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"}) throw e;
class InnerSession extends HashMap<Object, Object> implements HttpSession { }
private static final long serialVersionUID = -8679493647540628009L;
private HttpSession session;
public InnerSession(HttpSession session) {
this.session = session;
} }
// HashMap 相关方法处理 ---------------------------------------------------- @SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
/** public static class InnerSession extends HashMap<Object, Object> implements HttpSession {
* 覆盖 HashMap 的 put
*/
public Object put(Object name, Object value) {
session.setAttribute((String)name, value);
return null;
}
/** private static final long serialVersionUID = -8679493647540628009L;
* 覆盖 HashMap 的 get private HttpSession session;
*/
public Object get(Object name) {
return session.getAttribute((String)name);
}
// Session 相关方法处理 ---------------------------------------------------- public InnerSession(HttpSession session) {
public Object getAttribute(String key) { this.session = session;
return session.getAttribute(key); }
}
public Enumeration getAttributeNames() { // HashMap 相关方法处理 ----------------------------------------------------
return session.getAttributeNames(); /**
} * 覆盖 HashMap 的 put
*/
public Object put(Object name, Object value) {
session.setAttribute((String)name, value);
return null;
}
public long getCreationTime() { /**
return session.getCreationTime(); * 覆盖 HashMap 的 get
} */
public Object get(Object name) {
return session.getAttribute((String)name);
}
public String getId() { // Session 相关方法处理 ----------------------------------------------------
return session.getId(); public Object getAttribute(String key) {
} return session.getAttribute(key);
}
public long getLastAccessedTime() { public Enumeration getAttributeNames() {
return session.getLastAccessedTime(); return session.getAttributeNames();
} }
public int getMaxInactiveInterval() { public long getCreationTime() {
return session.getMaxInactiveInterval(); return session.getCreationTime();
} }
public ServletContext getServletContext() { public String getId() {
return session.getServletContext(); return session.getId();
} }
public javax.servlet.http.HttpSessionContext getSessionContext() { public long getLastAccessedTime() {
return session.getSessionContext(); return session.getLastAccessedTime();
} }
public Object getValue(String key) { public int getMaxInactiveInterval() {
return session.getValue(key); return session.getMaxInactiveInterval();
} }
public String[] getValueNames() { public ServletContext getServletContext() {
return session.getValueNames(); return session.getServletContext();
} }
public void invalidate() { public javax.servlet.http.HttpSessionContext getSessionContext() {
session.invalidate(); return session.getSessionContext();
} }
public boolean isNew() { public Object getValue(String key) {
return session.isNew(); return session.getValue(key);
} }
public void putValue(String key, Object value) { public String[] getValueNames() {
session.putValue(key, value); return session.getValueNames();
} }
public void removeAttribute(String key) { public void invalidate() {
session.removeAttribute(key); session.invalidate();
} }
public void removeValue(String key) { public boolean isNew() {
session.removeValue(key); return session.isNew();
} }
public void setAttribute(String key, Object value) { public void putValue(String key, Object value) {
session.setAttribute(key, value); session.putValue(key, value);
} }
public void setMaxInactiveInterval(int maxInactiveInterval) { public void removeAttribute(String key) {
session.setMaxInactiveInterval(maxInactiveInterval); session.removeAttribute(key);
}
public void removeValue(String key) {
session.removeValue(key);
}
public void setAttribute(String key, Object value) {
session.setAttribute(key, value);
}
public void setMaxInactiveInterval(int maxInactiveInterval) {
session.setMaxInactiveInterval(maxInactiveInterval);
}
public String toString() {
return session != null ? session.toString() : "null";
}
} }
} }
@@ -162,4 +179,3 @@ class InnerSession extends HashMap<Object, Object> implements HttpSession {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -46,15 +46,7 @@ public class ByteWriter extends Writer {
} }
public void close() { public void close() {
try { out = null;
if (out != null) {
out.flush();
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
out = null;
}
} }
public void write(String str, int offset, int len) throws IOException { public void write(String str, int offset, int len) throws IOException {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -40,15 +40,7 @@ public class CharWriter extends Writer {
} }
public void close() { public void close() {
try { out = null;
if (out != null) {
out.flush();
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
out = null;
}
} }
public void write(String str, int offset, int len) throws IOException { public void write(String str, int offset, int len) throws IOException {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -97,7 +97,7 @@ public class FastStringWriter extends Writer {
} }
static int MAX_SIZE = 1024 * 128; static int MAX_SIZE = 1024 * 64;
/** /**
* 由 StringWriter.close() 改造而来,原先该方法中无任何代码 ,改造如下: * 由 StringWriter.close() 改造而来,原先该方法中无任何代码 ,改造如下:
@@ -107,7 +107,7 @@ public class FastStringWriter extends Writer {
*/ */
public void close() { public void close() {
if (buf.length() > MAX_SIZE) { if (buf.length() > MAX_SIZE) {
buf = new StringBuilder(); // 释放空间占用过大的 buf buf = new StringBuilder(MAX_SIZE / 2); // 释放空间占用过大的 buf
} else { } else {
buf.setLength(0); buf.setLength(0);
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,12 +17,10 @@
package com.jfinal.template.source; package com.jfinal.template.source;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.JarURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLConnection;
import com.jfinal.template.EngineConfig; import com.jfinal.template.EngineConfig;
/** /**
@@ -65,24 +63,19 @@ public class ClassPathSource implements ISource {
this.classLoader = getClassLoader(); this.classLoader = getClassLoader();
this.url = classLoader.getResource(finalFileName); this.url = classLoader.getResource(finalFileName);
if (url == null) { if (url == null) {
throw new IllegalArgumentException("File not found : \"" + finalFileName + "\""); throw new IllegalArgumentException("File not found in CLASSPATH or JAR : \"" + finalFileName + "\"");
} }
processIsInJarAndlastModified(); processIsInJarAndlastModified();
} }
protected void processIsInJarAndlastModified() { protected void processIsInJarAndlastModified() {
try { if ("file".equalsIgnoreCase(url.getProtocol())) {
URLConnection conn = url.openConnection(); isInJar = false;
if ("jar".equals(url.getProtocol()) || conn instanceof JarURLConnection) { lastModified = new File(url.getFile()).lastModified();
isInJar = true; } else {
lastModified = -1; isInJar = true;
} else { lastModified = -1;
isInJar = false;
lastModified = conn.getLastModified();
}
} catch (IOException e) {
throw new RuntimeException(e);
} }
} }
@@ -111,7 +104,7 @@ public class ClassPathSource implements ISource {
return finalFileName; return finalFileName;
} }
public String getKey() { public String getCacheKey() {
return fileName; return fileName;
} }
@@ -120,12 +113,7 @@ public class ClassPathSource implements ISource {
} }
protected long getLastModified() { protected long getLastModified() {
try { return new File(url.getFile()).lastModified();
URLConnection conn = url.openConnection();
return conn.getLastModified();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
/** /**
@@ -152,9 +140,8 @@ public class ClassPathSource implements ISource {
public static StringBuilder loadFile(InputStream inputStream, String encoding) { public static StringBuilder loadFile(InputStream inputStream, String encoding) {
StringBuilder ret = new StringBuilder(); StringBuilder ret = new StringBuilder();
BufferedReader br = null;
try { try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, encoding))) {
br = new BufferedReader(new InputStreamReader(inputStream, encoding));
// br = new BufferedReader(new FileReader(fileName)); // br = new BufferedReader(new FileReader(fileName));
String line = br.readLine(); String line = br.readLine();
if (line != null) { if (line != null) {
@@ -170,16 +157,6 @@ public class ClassPathSource implements ISource {
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// com.jfinal.kit.LogKit.error(e.getMessage(), e);
e.printStackTrace();
}
}
}
} }
public String toString() { public String toString() {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ package com.jfinal.template.source;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import com.jfinal.template.EngineConfig; import com.jfinal.template.EngineConfig;
@@ -48,7 +47,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;
} }
@@ -77,6 +76,9 @@ public class FileSource implements ISource {
} }
private String buildFinalFileName(String baseTemplatePath, String fileName) { private String buildFinalFileName(String baseTemplatePath, String fileName) {
if (baseTemplatePath == null) {
return fileName;
}
char firstChar = fileName.charAt(0); char firstChar = fileName.charAt(0);
String finalFileName; String finalFileName;
if (firstChar == '/' || firstChar == '\\') { if (firstChar == '/' || firstChar == '\\') {
@@ -89,9 +91,8 @@ public class FileSource implements ISource {
public static StringBuilder loadFile(File file, String encoding) { public static StringBuilder loadFile(File file, String encoding) {
StringBuilder ret = new StringBuilder((int)file.length() + 3); StringBuilder ret = new StringBuilder((int)file.length() + 3);
BufferedReader br = null;
try { try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding))) {
br = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding));
// br = new BufferedReader(new FileReader(fileName)); // br = new BufferedReader(new FileReader(fileName));
String line = br.readLine(); String line = br.readLine();
if (line != null) { if (line != null) {
@@ -107,16 +108,6 @@ public class FileSource implements ISource {
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// com.jfinal.kit.LogKit.error(e.getMessage(), e);
e.printStackTrace();
}
}
}
} }
public String toString() { public String toString() {

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
package com.jfinal.template; package com.jfinal.template.stat;
import com.jfinal.template.expr.ast.ExprList; import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.stat.Location;
import com.jfinal.template.stat.ast.Output; import com.jfinal.template.stat.ast.Output;
/** /**
* IOutputDirectiveFactory * OutputDirectiveFactory
* 用于定制自定义输出指令替换系统默认输出指令满足个性化需求 * 用于定制自定义输出指令替换系统默认输出指令满足个性化需求
* *
* 用法 * 用法
@@ -32,12 +31,15 @@ import com.jfinal.template.stat.ast.Output;
* } * }
* *
* public void exec(Env env, Scope scope, Writer writer) { * public void exec(Env env, Scope scope, Writer writer) {
* write(writer, exprList.eval(scope)); * Object value = exprList.eval(scope);
* if (value != null) {
* write(writer, value.toString());
* }
* } * }
* } * }
* *
* 2定义 MyOutputDirectiveFactory * 2定义 MyOutputDirectiveFactory
* public class MyOutputDirectiveFactory implements IOutputDirectiveFactory { * public class MyOutputDirectiveFactory extends OutputDirectiveFactory {
* public Output getOutputDirective(ExprList exprList) { * public Output getOutputDirective(ExprList exprList) {
* return new MyOutput(exprList); * return new MyOutput(exprList);
* } * }
@@ -46,11 +48,13 @@ import com.jfinal.template.stat.ast.Output;
* 3配置 * 3配置
* engine.setOutputDirectiveFactory(new MyOutputDirectiveFactory()) * engine.setOutputDirectiveFactory(new MyOutputDirectiveFactory())
*/ */
public interface IOutputDirectiveFactory { public class OutputDirectiveFactory {
public Output getOutputDirective(ExprList exprList, Location location); public static final OutputDirectiveFactory me = new OutputDirectiveFactory();
public Output getOutputDirective(ExprList exprList, Location location) {
return new Output(exprList, location);
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -24,22 +24,7 @@ import com.jfinal.template.expr.ExprParser;
import com.jfinal.template.expr.ast.ExprList; import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.expr.ast.ForCtrl; import com.jfinal.template.expr.ast.ForCtrl;
import com.jfinal.template.stat.Symbol; import com.jfinal.template.stat.Symbol;
import com.jfinal.template.stat.ast.Break; import com.jfinal.template.stat.ast.*;
import com.jfinal.template.stat.ast.Call;
import com.jfinal.template.stat.ast.Continue;
import com.jfinal.template.stat.ast.Define;
import com.jfinal.template.stat.ast.Else;
import com.jfinal.template.stat.ast.ElseIf;
import com.jfinal.template.stat.ast.For;
import com.jfinal.template.stat.ast.If;
import com.jfinal.template.stat.ast.Include;
import com.jfinal.template.stat.ast.Return;
import com.jfinal.template.stat.ast.Set;
import com.jfinal.template.stat.ast.SetGlobal;
import com.jfinal.template.stat.ast.SetLocal;
import com.jfinal.template.stat.ast.Stat;
import com.jfinal.template.stat.ast.StatList;
import com.jfinal.template.stat.ast.Text;
/** /**
* DLRD (Double Layer Recursive Descent) Parser * DLRD (Double Layer Recursive Descent) Parser
@@ -90,7 +75,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;
} }
@@ -227,7 +212,41 @@ public class Parser {
case ELSE: case ELSE:
case END: case END:
case EOF: case EOF:
case CASE:
case DEFAULT:
return null; return null;
case SWITCH:
move();
para = matchPara(name);
Switch _switch = new Switch(parseExprList(para), getLocation(name.row));
CaseSetter currentCaseSetter = _switch;
for (Token currentToken=peek(); ; currentToken=peek()) {
if (currentToken.symbol == Symbol.CASE) {
move();
para = matchPara(currentToken);
statList = statList();
Case nextCase = new Case(parseExprList(para), statList, getLocation(currentToken.row));
currentCaseSetter.setNextCase(nextCase);
currentCaseSetter = nextCase;
} else if (currentToken.symbol == Symbol.DEFAULT) {
move();
statList = statList();
Default _default = new Default(statList);
_switch.setDefault(_default, getLocation(currentToken.row));
} else if (currentToken.symbol == Symbol.TEXT) {
TextToken tt = (TextToken)currentToken;
if (tt.getContent().toString().trim().length() != 0) {
throw new ParseException("Syntax error: expect #case or #default directive", getLocation(currentToken.row));
}
move();
} else {
break ;
}
}
matchEnd(name);
return _switch;
default : default :
throw new ParseException("Syntax error: can not match the token: " + name.value(), getLocation(name.row)); throw new ParseException("Syntax error: can not match the token: " + name.value(), getLocation(name.row));
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com). * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

Some files were not shown because too many files have changed in this diff Show More