collector) {
+ if (outputCompileError) {
+ super.outputCompileError(result, collector);
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java b/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
index dc3d806..69cd851 100644
--- a/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldGetters.java
@@ -68,7 +68,7 @@ public class FieldGetters {
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 (method.getName().equals(getterName) && method.getParameterCount() == 0) {
// if (MethodKit.isForbiddenMethod(getterName)) {
// throw new RuntimeException("Forbidden method: " + getterName);
// }
@@ -115,7 +115,7 @@ public class FieldGetters {
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) {
+ if (method.getName().equals(isMethodName) && method.getParameterCount() == 0) {
Class> returnType = method.getReturnType();
if (returnType == Boolean.class || returnType == boolean.class) {
return new IsMethodFieldGetter(method);
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java b/src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java
index 6231139..bfc924b 100644
--- a/src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldKeyBuilder.java
@@ -33,10 +33,14 @@ public abstract class FieldKeyBuilder {
}
/**
- * 设置为官方提供的 FastFieldKeyBuilder 实现,性能更高
+ * 开启 FastFieldKeyBuilder,性能更高
*/
- public static void setToFastFieldKeyBuilder() {
- instance = new FastFieldKeyBuilder();
+ public static void setFastFieldKeyBuilder(boolean enable) {
+ if (enable) {
+ instance = new FastFieldKeyBuilder();
+ } else {
+ instance = new StrictFieldKeyBuilder();
+ }
}
/**
diff --git a/src/main/java/com/jfinal/template/expr/ast/FieldKit.java b/src/main/java/com/jfinal/template/expr/ast/FieldKit.java
index fab3fc7..8ce0c43 100644
--- a/src/main/java/com/jfinal/template/expr/ast/FieldKit.java
+++ b/src/main/java/com/jfinal/template/expr/ast/FieldKit.java
@@ -140,6 +140,36 @@ public class FieldKit {
public static void clearCache() {
fieldGetterCache.clear();
}
+
+ /**
+ * 设置极速模式
+ *
+ * 极速模式将生成代理对象来消除 java.lang.reflect.Method.invoke(...) 调用,
+ * 性能提升 12.9%
+ */
+ public static synchronized void setFastMode(boolean fastMode) {
+ if (fastMode) {
+ if ( !contains(FastFieldGetter.class) ) {
+ addFieldGetterToFirst(new FastFieldGetter());
+ }
+ } else {
+ if (contains(FastFieldGetter.class)) {
+ removeFieldGetter(FastFieldGetter.class);
+ }
+ }
+ }
+
+ /**
+ * 判断是否包含某个 FieldGetter
+ */
+ public static boolean contains(Class extends FieldGetter> fieldGetterClass) {
+ for (FieldGetter fg : getters) {
+ if (fg.getClass() == fieldGetterClass) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/jfinal/template/ext/directive/EscapeDirective.java b/src/main/java/com/jfinal/template/ext/directive/EscapeDirective.java
index 1940d98..92ffb64 100644
--- a/src/main/java/com/jfinal/template/ext/directive/EscapeDirective.java
+++ b/src/main/java/com/jfinal/template/ext/directive/EscapeDirective.java
@@ -16,9 +16,12 @@
package com.jfinal.template.ext.directive;
+import java.io.IOException;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
+import com.jfinal.template.TemplateException;
import com.jfinal.template.io.Writer;
+import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;
/**
@@ -29,45 +32,67 @@ import com.jfinal.template.stat.Scope;
public class EscapeDirective extends Directive {
public void exec(Env env, Scope scope, Writer writer) {
- Object value = exprList.eval(scope);
- if (value != null) {
- write(writer, escape(value.toString()));
+ try {
+ Object value = exprList.eval(scope);
+
+ if (value instanceof String) {
+ escape((String)value, writer);
+ } else if (value instanceof Number) {
+ Class> c = value.getClass();
+ if (c == Integer.class) {
+ writer.write((Integer)value);
+ } else if (c == Long.class) {
+ writer.write((Long)value);
+ } else if (c == Double.class) {
+ writer.write((Double)value);
+ } else if (c == Float.class) {
+ writer.write((Float)value);
+ } else if (c == Short.class) {
+ writer.write((Short)value);
+ } else {
+ writer.write(value.toString());
+ }
+ } else if (value != null) {
+ escape(value.toString(), writer);
+ }
+ } catch(TemplateException | ParseException e) {
+ throw e;
+ } catch(Exception e) {
+ throw new TemplateException(e.getMessage(), location, e);
}
}
- // TODO 挪到 StrKit 中
- private String escape(String str) {
- if (str == null || str.length() == 0) {
- return str;
+ private void escape(String str, Writer w) throws IOException {
+ int len = str.length();
+ if (len == 0) {
+ return ;
}
- int len = str.length();
- StringBuilder ret = new StringBuilder(len * 2);
for (int i = 0; i < len; i++) {
char cur = str.charAt(i);
switch (cur) {
case '<':
- ret.append("<");
+ w.write("<");
break;
case '>':
- ret.append(">");
+ w.write(">");
break;
case '"':
- ret.append(""");
+ w.write(""");
break;
case '\'':
- // ret.append("'"); // IE 不支持 ' 考虑 '
- ret.append("'");
+ // w.write("'"); // IE 不支持 ' 考虑 '
+ w.write("'");
break;
case '&':
- ret.append("&");
+ w.write("&");
break;
default:
- ret.append(cur);
+ w.write(str, i, 1);
break;
}
}
-
- return ret.toString();
}
}
+
+
diff --git a/src/main/java/com/jfinal/template/io/FastStringWriter.java b/src/main/java/com/jfinal/template/io/FastStringWriter.java
index 8eb6bb7..85e47e5 100644
--- a/src/main/java/com/jfinal/template/io/FastStringWriter.java
+++ b/src/main/java/com/jfinal/template/io/FastStringWriter.java
@@ -16,102 +16,153 @@
package com.jfinal.template.io;
+import java.io.IOException;
import java.io.Writer;
/**
* FastStringWriter
*
*
- * 由 JDK 中 StringWriter 改造而来,在其基础之上做了如下改变:
- * 1:StringBuffer 属性改为 StringBuilder,避免了前者的 synchronized 操作
- * 2:添加了 MAX_SIZE 属性
- * 3:去掉了 close() 方法声明中的 throws IOException,并添加了代码,原先该方法中无任何代码
+ * 由 JDK 中 Writer 改造而来,在其基础之上做了如下改变:
+ * 1:添加 char[] value 直接保存 char 值
+ * 2:添加 int len 记录数据长度
+ * 3:去掉 synchronized 操作
+ * 4:添加 MAX_BUFFER_SIZE,限定 value 被重用的最大长度
+ * 5:去掉了 close() 方法声明中的 throws IOException,并添加缓存回收逻辑
*
*/
public class FastStringWriter extends Writer {
- private StringBuilder buf;
+ private char[] value;
+ private int len;
- public FastStringWriter() {
- buf = new StringBuilder();
- }
-
- public FastStringWriter(int initialSize) {
- if (initialSize < 0) {
- throw new IllegalArgumentException("Negative buffer size");
- }
- buf = new StringBuilder(initialSize);
- }
-
- public void write(int c) {
- buf.append((char) c);
- }
-
- public void write(char cbuf[], int off, int len) {
- if ((off < 0) || (off > cbuf.length) || (len < 0) ||
- ((off + len) > cbuf.length) || ((off + len) < 0)) {
- throw new IndexOutOfBoundsException();
- } else if (len == 0) {
- return;
- }
- buf.append(cbuf, off, len);
- }
-
- public void write(String str) {
- buf.append(str);
- }
-
- public void write(String str, int off, int len) {
- buf.append(str.substring(off, off + len));
- }
-
- public FastStringWriter append(CharSequence csq) {
- if (csq == null) {
- write("null");
- } else {
- write(csq.toString());
- }
- return this;
- }
-
- public FastStringWriter append(CharSequence csq, int start, int end) {
- CharSequence cs = (csq == null ? "null" : csq);
- write(cs.subSequence(start, end).toString());
- return this;
- }
-
- public FastStringWriter append(char c) {
- write(c);
- return this;
- }
-
- public String toString() {
- return buf.toString();
- }
-
- public StringBuilder getBuffer() {
- return buf;
- }
-
- public void flush() {
-
- }
-
- static int MAX_SIZE = 1024 * 64;
-
- /**
- * 由 StringWriter.close() 改造而来,原先该方法中无任何代码 ,改造如下:
- * 1:去掉 throws IOException
- * 2:添加 buf 空间释放处理逻辑
- * 3:添加 buf.setLength(0),以便于配合 ThreadLocal 回收利用
- */
- public void close() {
- if (buf.length() > MAX_SIZE) {
- buf = new StringBuilder(MAX_SIZE / 2); // 释放空间占用过大的 buf
- } else {
- buf.setLength(0);
+ private static int MAX_BUFFER_SIZE = 1024 * 256; // 1024 * 64;
+
+ public static void setMaxBufferSize(int maxBufferSize) {
+ int min = 256;
+ if (maxBufferSize < min) {
+ throw new IllegalArgumentException("maxBufferSize must more than " + min);
}
- }
+ MAX_BUFFER_SIZE = maxBufferSize;
+ }
+
+ @Override
+ public void close() /* throws IOException */ {
+ len = 0;
+
+ // 释放空间占用过大的缓存
+ if (value.length > MAX_BUFFER_SIZE) {
+ value = new char[Math.max(256, MAX_BUFFER_SIZE / 2)];
+ }
+ }
+
+ public String toString() {
+ return new String(value, 0, len);
+ }
+
+ public StringBuilder toStringBuilder() {
+ return new StringBuilder(len + 64).append(value, 0, len);
+ }
+
+ public FastStringWriter(int capacity) {
+ value = new char[capacity];
+ }
+
+ public FastStringWriter() {
+ this(128);
+ }
+
+ /**
+ * 扩容
+ */
+ protected void expandCapacity(int newLen) {
+ int newCapacity = Math.max(newLen, value.length * 2);
+ char[] newValue = new char[newCapacity];
+
+ // 复制 value 中的值到 newValue
+ if (len > 0) {
+ System.arraycopy(value, 0, newValue, 0, len);
+ }
+ value = newValue;
+ }
+
+ @Override
+ public void write(char buffer[], int offset, int len) throws IOException {
+ int newLen = this.len + len;
+ if (newLen > value.length) {
+ expandCapacity(newLen);
+ }
+
+ System.arraycopy(buffer, offset, value, this.len, len);
+ this.len = newLen;
+ }
+
+ @Override
+ public void write(String str, int offset, int len) throws IOException {
+ int newLen = this.len + len;
+ if (newLen > value.length) {
+ expandCapacity(newLen);
+ }
+
+ str.getChars(offset, (offset + len), value, this.len);
+ this.len = newLen;
+ }
+
+ @Override
+ public void write(int c) throws IOException {
+ char[] buffer = {(char)c};
+ write(buffer, 0, 1);
+ }
+
+ @Override
+ public void write(char buffer[]) throws IOException {
+ write(buffer, 0, buffer.length);
+ }
+
+ @Override
+ public void write(String str) throws IOException {
+ write(str, 0, str.length());
+ }
+
+ @Override
+ public Writer append(CharSequence csq) throws IOException {
+ if (csq instanceof String) {
+ String str = (String)csq;
+ write(str, 0, str.length());
+ return this;
+ }
+
+ if (csq == null)
+ write("null");
+ else
+ write(csq.toString());
+ return this;
+ }
+
+ @Override
+ public Writer append(CharSequence csq, int start, int end) throws IOException {
+ if (csq instanceof String) {
+ String str = (String)csq;
+ write(str, start, (end - start));
+ return this;
+ }
+
+ CharSequence cs = (csq == null ? "null" : csq);
+ write(cs.subSequence(start, end).toString());
+ return this;
+ }
+
+ @Override
+ public Writer append(char c) throws IOException {
+ char[] buffer = {c};
+ write(buffer, 0, 1);
+ return this;
+ }
+
+ @Override
+ public void flush() throws IOException {
+
+ }
}
diff --git a/src/main/java/com/jfinal/template/stat/ast/Output.java b/src/main/java/com/jfinal/template/stat/ast/Output.java
index dd2bfb9..b2c0e4d 100644
--- a/src/main/java/com/jfinal/template/stat/ast/Output.java
+++ b/src/main/java/com/jfinal/template/stat/ast/Output.java
@@ -66,8 +66,6 @@ public class Output extends Stat {
} else {
writer.write(value.toString());
}
- } else if (value instanceof Boolean) {
- writer.write((Boolean)value);
} else if (value != null) {
writer.write(value.toString());
}