From 3583a4bb33a8af40c425f0257e41fd3f46791ba3 Mon Sep 17 00:00:00 2001 From: xiaoma20082008 Date: Tue, 23 Nov 2021 15:44:00 +0800 Subject: [PATCH] : refactor the velocity-engine to be more scalable --- pom.xml | 1 + velocity-engine-core/README.md | 62 ++ velocity-engine-core/pom.xml | 6 + .../java/org/apache/velocity/api/Node.java | 8 + .../org/apache/velocity/api/Resource.java | 99 ++++ .../org/apache/velocity/api/Template.java | 13 + .../apache/velocity/app/VelocityEngine.java | 19 +- .../java/org/apache/velocity/spi/Cache.java | 25 + .../org/apache/velocity/spi/Compiler.java | 28 + .../org/apache/velocity/spi/Converter.java | 25 + .../java/org/apache/velocity/spi/Loader.java | 31 + .../java/org/apache/velocity/spi/Parser.java | 29 + .../org/apache/velocity/spi/Translator.java | 29 + .../apache/velocity/spi/caches/MapCache.java | 116 ++++ .../velocity/spi/compilers/BaseCompiler.java | 111 ++++ .../velocity/spi/compilers/JavacCompiler.java | 306 ++++++++++ .../spi/compilers/JavassistCompiler.java | 30 + .../spi/converters/MultiConverter.java | 42 ++ .../velocity/spi/converters/XssConverter.java | 68 +++ .../velocity/spi/loaders/BaseLoader.java | 153 +++++ .../velocity/spi/loaders/ClasspathLoader.java | 39 ++ .../spi/loaders/DataSourceLoader.java | 39 ++ .../velocity/spi/loaders/FileLoader.java | 40 ++ .../velocity/spi/loaders/JarLoader.java | 40 ++ .../velocity/spi/loaders/MultiLoader.java | 66 +++ .../velocity/spi/loaders/UrlLoader.java | 37 ++ .../velocity/spi/loaders/ZipLoader.java | 37 ++ .../spi/loaders/resources/BaseResource.java | 84 +++ .../loaders/resources/ClasspathResource.java | 40 ++ .../loaders/resources/DataSourceResource.java | 46 ++ .../spi/loaders/resources/FileResource.java | 41 ++ .../resources/InputStreamResource.java | 82 +++ .../spi/loaders/resources/JarResource.java | 42 ++ .../spi/loaders/resources/StringResource.java | 57 ++ .../spi/loaders/resources/UrlResource.java | 42 ++ .../spi/loaders/resources/ZipResource.java | 41 ++ .../velocity/spi/methods/LangMethod.java | 24 + .../velocity/spi/parsers/TemplateParser.java | 43 ++ .../spi/translators/CompiledTranslator.java | 49 ++ .../translators/InterpretedTranslator.java | 35 ++ .../translators/templates/BaseTemplate.java | 140 +++++ .../templates/CompiledTemplate.java | 36 ++ .../templates/CompiledVisitor.java | 547 ++++++++++++++++++ .../templates/InterpretedTemplate.java | 47 ++ .../templates/InterpretedVisitor.java | 37 ++ .../org/apache/velocity/util/ClassUtils.java | 15 +- .../util/DeprecationAwareExtProperties.java | 3 +- .../spi/compilers/JavacCompilerTest.java | 12 + .../spi/compilers/JavassistCompilerTest.java | 12 + .../spi/loaders/ClasspathLoaderTest.java | 32 + .../translators/CompiledTranslatorTest.java | 62 ++ 51 files changed, 3059 insertions(+), 9 deletions(-) create mode 100644 velocity-engine-core/README.md create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/api/Node.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/api/Resource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/api/Template.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/Cache.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/Compiler.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/Converter.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/Loader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/Parser.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/Translator.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/caches/MapCache.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/BaseCompiler.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavacCompiler.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavassistCompiler.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/MultiConverter.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/XssConverter.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/BaseLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ClasspathLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/DataSourceLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/FileLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/JarLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/MultiLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/UrlLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ZipLoader.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/BaseResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ClasspathResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/DataSourceResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/FileResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/InputStreamResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/JarResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/StringResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/UrlResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ZipResource.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/methods/LangMethod.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/parsers/TemplateParser.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/CompiledTranslator.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/InterpretedTranslator.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/BaseTemplate.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledTemplate.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledVisitor.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedTemplate.java create mode 100644 velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedVisitor.java create mode 100644 velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavacCompilerTest.java create mode 100644 velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavassistCompilerTest.java create mode 100644 velocity-engine-core/src/test/java/org/apache/velocity/spi/loaders/ClasspathLoaderTest.java create mode 100644 velocity-engine-core/src/test/java/org/apache/velocity/spi/translators/CompiledTranslatorTest.java diff --git a/pom.xml b/pom.xml index 4432c60c3..2aa90db7b 100644 --- a/pom.xml +++ b/pom.xml @@ -137,6 +137,7 @@ org.apache.maven.plugins maven-compiler-plugin + 3.8.1 true true diff --git a/velocity-engine-core/README.md b/velocity-engine-core/README.md new file mode 100644 index 000000000..162762ecc --- /dev/null +++ b/velocity-engine-core/README.md @@ -0,0 +1,62 @@ +``` + +----------+ | + | | nowContext +---------+ | + | | -----------> | Context | <--+ render + init | | +---------+ | + ----->| Velocity | v + | | getTemplate +------------------+ + | | ------------> | Template | + | | +------------------+ + +----------+ +``` + +```java +import java.io.StringReader; +import java.io.Writer; +import org.apache.velocity.Template; +import org.apache.velocity.spi.Translator; +import org.apache.velocity.runtime.parser.Parser; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.runtime.resource.Resource; +import org.apache.velocity.runtime.resource.ResourceManager; +import org.apache.velocity.runtime.resource.loader.ResourceLoader; + +public class View { + + /** + * render the template + * + * @param name template name + * @param writer out + */ + public void render(String name, Writer writer) { + // 1. get template + Template template = Velocity.getTemplate(name, "utf-8"); + // 2. get current context + Context context = Velocity.nowContext(); + // 3. render template + template.render(context, writer); + } +} + +public class Velocity { + + private ResourceManager resourceManager; + private Parser parser; + private Translator translator; + private ResourceLoader loader; + + public Template getTemplate(String name, String encoding) { + String path = toPath(name); + Resource resource = resourceManager.getResource(path, ResourceManager.RESOURCE_TEMPLATE, encoding); + SimpleNode node = parser.parse(new StringReader((String) resource.getData()), new Template()); + return translator.translate(resource, node); + } + + private String toPath(String name) { + return "suffix/" + name; + } + +} + +``` diff --git a/velocity-engine-core/pom.xml b/velocity-engine-core/pom.xml index bec9c9a51..95bbeb079 100644 --- a/velocity-engine-core/pom.xml +++ b/velocity-engine-core/pom.xml @@ -295,6 +295,12 @@ + + org.javassist + javassist + 3.28.0-GA + provided + org.apache.commons commons-lang3 diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/api/Node.java b/velocity-engine-core/src/main/java/org/apache/velocity/api/Node.java new file mode 100644 index 000000000..dfa63acbb --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/api/Node.java @@ -0,0 +1,8 @@ +package org.apache.velocity.api; + +import org.apache.velocity.runtime.visitor.BaseVisitor; + +public interface Node { + + void accept(BaseVisitor visitor); +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/api/Resource.java b/velocity-engine-core/src/main/java/org/apache/velocity/api/Resource.java new file mode 100644 index 000000000..0873a5284 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/api/Resource.java @@ -0,0 +1,99 @@ +package org.apache.velocity.api; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Locale; +import org.apache.velocity.runtime.RuntimeInstance; + +public interface Resource { + + /** + * Get the resource name. + * + * @return name + */ + String getName(); + + /** + * Get the resource encoding. + * + * @return encoding + */ + String getEncoding(); + + /** + * Get the resource locale. + * + * @return locale + */ + Locale getLocale(); + + /** + * Get the resource last modified time. + * + * @return last modified time + */ + long getLastModified(); + + /** + * Get the resource length. + * + * @return source length + */ + long getLength(); + + /** + * Get the template source. + * + * @return source + * @throws IOException - If an I/O error occurs + */ + String getSource() throws IOException; + + /** + * Get the template source reader. + *

+ * NOTE: Don't forget close the reader. + *

+ *

+   * Reader reader = resource.openReader();
+   * try {
+   * 	 // do something ...
+   * } finally {
+   * 	 reader.close();
+   * }
+   * 
+ * + * @return source reader + * @throws IOException - If an I/O error occurs + */ + Reader openReader() throws IOException; + + /** + * Get the template source input stream. + *

+ * NOTE: Don't forget close the input stream. + *

+ *

+   * InputStream stream = resource.openStream();
+   * try {
+   * 	 // do something ...
+   * } finally {
+   * 	 stream.close();
+   * }
+   * 
+ * + * @return source input stream + * @throws IOException - If an I/O error occurs + */ + InputStream openStream() throws IOException; + + /** + * Get the template engine. + * + * @return engine + */ + RuntimeInstance getEngine(); + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/api/Template.java b/velocity-engine-core/src/main/java/org/apache/velocity/api/Template.java new file mode 100644 index 000000000..ccf87f811 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/api/Template.java @@ -0,0 +1,13 @@ +package org.apache.velocity.api; + +import java.io.IOException; +import java.io.Writer; +import java.text.ParseException; +import org.apache.velocity.context.Context; + +public interface Template extends Resource, Node { + + void render(Context context, Writer out) throws IOException, ParseException; + + Object evaluate(Context context) throws ParseException; +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java index 93751f3a7..2d5295038 100644 --- a/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java @@ -19,6 +19,10 @@ * under the License. */ +import java.io.Reader; +import java.io.Writer; +import java.util.Objects; +import java.util.Properties; import org.apache.velocity.Template; import org.apache.velocity.context.Context; import org.apache.velocity.exception.MethodInvocationException; @@ -28,10 +32,6 @@ import org.apache.velocity.runtime.RuntimeInstance; import org.slf4j.Logger; -import java.io.Reader; -import java.io.Writer; -import java.util.Properties; - /** *

* This class provides a separate new-able instance of the @@ -51,14 +51,19 @@ */ public class VelocityEngine implements RuntimeConstants { - private RuntimeInstance ri = new RuntimeInstance(); + private final RuntimeInstance ri; /** * Init-less CTOR */ public VelocityEngine() { - // do nothing + this(new RuntimeInstance()); + } + + public VelocityEngine(RuntimeInstance ri) // for testable + { + this.ri = Objects.requireNonNull(ri, "RuntimeInstance is null"); } /** @@ -68,6 +73,7 @@ public VelocityEngine() */ public VelocityEngine(String propsFilename) { + this(); ri.setProperties(propsFilename); } @@ -77,6 +83,7 @@ public VelocityEngine(String propsFilename) */ public VelocityEngine(Properties p) { + this(); ri.setProperties(p); } diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/Cache.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Cache.java new file mode 100644 index 000000000..0ebb8d996 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Cache.java @@ -0,0 +1,25 @@ +package org.apache.velocity.spi; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.concurrent.ConcurrentMap; + +public interface Cache extends ConcurrentMap { +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/Compiler.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Compiler.java new file mode 100644 index 000000000..656781200 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Compiler.java @@ -0,0 +1,28 @@ +package org.apache.velocity.spi; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.velocity.exception.VelocityException; + +public interface Compiler { + + Class compile(String source) throws VelocityException; + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/Converter.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Converter.java new file mode 100644 index 000000000..4a38ee538 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Converter.java @@ -0,0 +1,25 @@ +package org.apache.velocity.spi; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +public interface Converter { + + String convert(String key, String value); +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/Loader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Loader.java new file mode 100644 index 000000000..9cf1c953f --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Loader.java @@ -0,0 +1,31 @@ +package org.apache.velocity.spi; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; + +public interface Loader { + + boolean exists(String name, Locale locale); + + Resource load(String name, Locale locale, String encoding) throws IOException; +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/Parser.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Parser.java new file mode 100644 index 000000000..dac0cd6db --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Parser.java @@ -0,0 +1,29 @@ +package org.apache.velocity.spi; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + + +import org.apache.velocity.runtime.parser.ParseException; +import org.apache.velocity.runtime.parser.node.SimpleNode; + +public interface Parser { + + SimpleNode parse(String name, String source) throws ParseException; +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/Translator.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Translator.java new file mode 100644 index 000000000..f3b86483d --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/Translator.java @@ -0,0 +1,29 @@ +package org.apache.velocity.spi; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.velocity.api.Resource; +import org.apache.velocity.api.Template; +import org.apache.velocity.runtime.parser.node.SimpleNode; + +public interface Translator { + + Template translate(Resource resource, SimpleNode root); +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/caches/MapCache.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/caches/MapCache.java new file mode 100644 index 000000000..839ce74aa --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/caches/MapCache.java @@ -0,0 +1,116 @@ +package org.apache.velocity.spi.caches; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import org.apache.velocity.spi.Cache; + +public class MapCache implements Cache { + + private final ConcurrentMap map; + + public MapCache() { + this.map = new ConcurrentHashMap<>(); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public V get(Object key) { + return map.get(key); + } + + @Override + public V put(K key, V value) { + return map.put(key, value); + } + + @Override + public V remove(Object key) { + return map.remove(key); + } + + @Override + public void putAll(Map m) { + map.putAll(m); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public Set> entrySet() { + return map.entrySet(); + } + + @Override + public V putIfAbsent(K key, V value) { + return map.putIfAbsent(key, value); + } + + @Override + public boolean remove(Object key, Object value) { + return map.remove(key, value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + return map.replace(key, oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + return map.replace(key, value); + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/BaseCompiler.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/BaseCompiler.java new file mode 100644 index 000000000..5cc551618 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/BaseCompiler.java @@ -0,0 +1,111 @@ +package org.apache.velocity.spi.compilers; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.ParseException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.spi.Cache; +import org.apache.velocity.spi.Compiler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class BaseCompiler implements Compiler { + + private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\\s+([_a-zA-Z][_a-zA-Z0-9\\.]*);"); + private static final Pattern CLASS_PATTERN = Pattern.compile("class\\s+([_a-zA-Z][_a-zA-Z0-9]*)\\s+"); + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected Cache cache; + protected File compileDirectory; + private volatile boolean first = true; + + @Override + public Class compile(String code) throws VelocityException { + String className = null; + try { + code = code.trim(); + if (!code.endsWith("}")) { + throw new ParseException("The java code not endsWith \"}\"", code.length() - 1); + } + Matcher matcher = PACKAGE_PATTERN.matcher(code); + String pkg; + if (matcher.find()) { + pkg = matcher.group(1); + } else { + pkg = ""; + } + matcher = CLASS_PATTERN.matcher(code); + String classSimpleName; + if (matcher.find()) { + classSimpleName = matcher.group(1); + } else { + throw new VelocityException("No such class name in java code."); + } + className = StringUtils.isNotEmpty(pkg) ? pkg + "." + classSimpleName : classSimpleName; + return doCompile(className, code); + } catch (Throwable t) { + if (logger != null && logger.isErrorEnabled()) { + logger.error("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + + ", code: \n================================\n" + code + "\n================================\n", t); + } + if (t instanceof VelocityException) { + throw (VelocityException) t; + } + throw new VelocityException( + "Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", stack: ", t); + } + } + + protected abstract Class doCompile(String name, String source) throws Exception; + + public void setCache(Cache cache) { + this.cache = cache; + } + + protected void saveBytecode(String name, byte[] bytecode) { + if (compileDirectory != null) { + try { + File file = new File(compileDirectory, name.replace('.', '/') + ".class"); + FileOutputStream out = new FileOutputStream(file); + try { + out.write(bytecode); + out.flush(); + } finally { + out.close(); + } + if (first) { + first = false; + if (logger != null && logger.isInfoEnabled()) { + logger.info("Compile template classes to directory " + compileDirectory.getAbsolutePath()); + } + } + } catch (IOException e) { + logger.warn(e.getMessage(), e); + } + } + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavacCompiler.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavacCompiler.java new file mode 100644 index 000000000..a69f0c6f3 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavacCompiler.java @@ -0,0 +1,306 @@ +package org.apache.velocity.spi.compilers; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.tools.DiagnosticCollector; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; +import org.apache.velocity.util.ClassUtils; + +public class JavacCompiler extends BaseCompiler { + + private final JavaCompiler compiler; + private final DiagnosticCollector diagnosticCollector; + private final StandardJavaFileManager standardJavaFileManager; + private final ClassLoaderImpl classLoader; + private final JavaFileManagerImpl javaFileManager; + private final List options = new ArrayList<>(); + private final List lintOptions = new ArrayList<>(); + + public JavacCompiler() { + compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + throw new IllegalStateException( + "Can not get system java compiler."); + } + diagnosticCollector = new DiagnosticCollector(); + standardJavaFileManager = compiler.getStandardFileManager(diagnosticCollector, null, null); + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + try { + contextLoader.loadClass(JavacCompiler.class.getName()); + } catch (ClassNotFoundException e) { + contextLoader = JavacCompiler.class.getClassLoader(); + } + ClassLoader loader = contextLoader; + Set files = new HashSet<>(); + while (loader instanceof URLClassLoader + && (!loader.getClass().getName().equals("sun.misc.Launcher$AppClassLoader"))) { + URLClassLoader urlClassLoader = (URLClassLoader) loader; + for (URL url : urlClassLoader.getURLs()) { + files.add(new File(url.getFile())); + } + loader = loader.getParent(); + } + if (files.size() > 0) { + try { + Iterable list = standardJavaFileManager.getLocation(StandardLocation.CLASS_PATH); + for (File file : list) { + files.add(file); + } + standardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, files); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + final ClassLoader parentLoader = contextLoader; + classLoader = AccessController.doPrivileged(new PrivilegedAction() { + public ClassLoaderImpl run() { + return new ClassLoaderImpl(parentLoader); + } + }); + javaFileManager = new JavaFileManagerImpl(standardJavaFileManager, classLoader); + lintOptions.add("-Xlint:unchecked"); + } + + @Override + protected Class doCompile(String name, String source) throws Exception { + return doCompile(name, source, options); + } + + private Class doCompile(String name, String sourceCode, List options) throws Exception { + try { + return classLoader.loadClass(name); + } catch (ClassNotFoundException e) { + int i = name.lastIndexOf('.'); + String packageName = i < 0 ? "" : name.substring(0, i); + String className = i < 0 ? name : name.substring(i + 1); + JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode); + javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, + className + ClassUtils.JAVA_EXTENSION, javaFileObject); + Boolean result = compiler.getTask( + null, + javaFileManager, + diagnosticCollector, + options, + null, + Collections.singletonList(javaFileObject) + ).call(); + if (result == null || !result) { + throw new IllegalStateException( + "Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector.getDiagnostics()); + } + if (compileDirectory != null) { + saveBytecode(name, javaFileObject.getByteCode()); + } + return classLoader.loadClass(name); + } + } + + private static final class JavaFileObjectImpl extends SimpleJavaFileObject { + + private final CharSequence source; + private ByteArrayOutputStream bytecode; + + public JavaFileObjectImpl(final String baseName, final CharSequence source) { + super(ClassUtils.toURI(baseName + ClassUtils.JAVA_EXTENSION), Kind.SOURCE); + this.source = source; + } + + public JavaFileObjectImpl(final String name, final Kind kind) { + super(ClassUtils.toURI(name), kind); + source = null; + } + + public JavaFileObjectImpl(URI uri, Kind kind) { + super(uri, kind); + source = null; + } + + @Override + public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException { + if (source == null) { + throw new UnsupportedOperationException("source == null"); + } + return source; + } + + @Override + public InputStream openInputStream() { + return new ByteArrayInputStream(getByteCode()); + } + + @Override + public OutputStream openOutputStream() { + return bytecode = new ByteArrayOutputStream(); + } + + public byte[] getByteCode() { + return bytecode.toByteArray(); + } + } + + private static final class JavaFileManagerImpl extends ForwardingJavaFileManager { + + private final ClassLoaderImpl classLoader; + private final Map fileObjects = new HashMap(); + + public JavaFileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) { + super(fileManager); + this.classLoader = classLoader; + } + + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + FileObject o = fileObjects.get(uri(location, packageName, relativeName)); + if (o != null) { + return o; + } + return super.getFileForInput(location, packageName, relativeName); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, Kind kind, FileObject fo) + throws IOException { + JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind); + classLoader.add(qualifiedName, file); + return file; + } + + @Override + public ClassLoader getClassLoader(JavaFileManager.Location location) { + return classLoader; + } + + @Override + public String inferBinaryName(Location loc, JavaFileObject file) { + if (file instanceof JavaFileObjectImpl) { + return file.getName(); + } + return super.inferBinaryName(loc, file); + } + + @Override + public Iterable list(Location location, String packageName, Set kinds, boolean recurse) + throws IOException { + ArrayList files = new ArrayList(); + if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) { + for (JavaFileObject file : fileObjects.values()) { + if (file.getKind() == Kind.CLASS && file.getName().startsWith(packageName)) { + files.add(file); + } + } + files.addAll(classLoader.files()); + } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) { + for (JavaFileObject file : fileObjects.values()) { + if (file.getKind() == Kind.SOURCE && file.getName().startsWith(packageName)) { + files.add(file); + } + } + } + Iterable result = super.list(location, packageName, kinds, recurse); + for (JavaFileObject file : result) { + files.add(file); + } + return files; + } + + public void putFileForInput(StandardLocation location, String packageName, String relativeName, + JavaFileObject file) { + fileObjects.put(uri(location, packageName, relativeName), file); + } + + private URI uri(Location location, String packageName, String relativeName) { + return ClassUtils.toURI(location.getName() + '/' + packageName + '/' + relativeName); + } + + } + + private static final class ClassLoaderImpl extends ClassLoader { + + private final Map classes = new HashMap(); + + ClassLoaderImpl(final ClassLoader parentClassLoader) { + super(parentClassLoader); + } + + Collection files() { + return Collections.unmodifiableCollection(classes.values()); + } + + @Override + protected Class findClass(final String qualifiedClassName) throws ClassNotFoundException { + try { + return super.findClass(qualifiedClassName); + } catch (ClassNotFoundException e) { + JavaFileObject file = classes.get(qualifiedClassName); + if (file != null) { + byte[] bytes = ((JavaFileObjectImpl) file).getByteCode(); + return defineClass(qualifiedClassName, bytes, 0, bytes.length); + } + throw e; + } + } + + @Override + public InputStream getResourceAsStream(final String name) { + if (name.endsWith(ClassUtils.CLASS_EXTENSION)) { + String qualifiedClassName = name.substring(0, name.length() - ClassUtils.CLASS_EXTENSION.length()) + .replace('/', '.'); + JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName); + if (file != null) { + return new ByteArrayInputStream(file.getByteCode()); + } + } + return super.getResourceAsStream(name); + } + + public void add(final String qualifiedClassName, final JavaFileObject javaFile) { + classes.put(qualifiedClassName, javaFile); + } + + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavassistCompiler.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavassistCompiler.java new file mode 100644 index 000000000..5eba7a175 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/compilers/JavassistCompiler.java @@ -0,0 +1,30 @@ +package org.apache.velocity.spi.compilers; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import javassist.ClassPool; + +public class JavassistCompiler extends BaseCompiler { + + @Override + protected Class doCompile(String name, String source) throws Exception { + return ClassPool.getDefault().makeClass(source).toClass(); + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/MultiConverter.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/MultiConverter.java new file mode 100644 index 000000000..a7b4bd5df --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/MultiConverter.java @@ -0,0 +1,42 @@ +package org.apache.velocity.spi.converters; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.velocity.spi.Converter; + +public class MultiConverter implements Converter { + + private Converter[] converters; + + @Override + public String convert(String key, String value) { + if (converters == null) { + return value; + } + for (Converter converter : converters) { + value = converter.convert(key, value); + } + return value; + } + + public void setConverters(Converter[] converters) { + this.converters = converters; + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/XssConverter.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/XssConverter.java new file mode 100644 index 000000000..ade359df8 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/converters/XssConverter.java @@ -0,0 +1,68 @@ +package org.apache.velocity.spi.converters; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.spi.Converter; + +public class XssConverter implements Converter { + + @Override + public String convert(String key, String value) { + if (StringUtils.isBlank(value)) { + return value; + } + char[] array = value.toCharArray(); + int length = array.length; + StringBuilder out = new StringBuilder(length); + int i = 0; + while (i < length) { + char ch = array[i++]; + switch (ch) { + case '>': { + out.append(">"); + break; + } + case '<': { + out.append("<"); + break; + } + case '\"': { + out.append("""); + break; + } + case '\'': { + out.append("'"); + break; + } + case '&': { + out.append("&"); + break; + } + default: { + out.append(ch); + break; + } + } + } + return out.toString(); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/BaseLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/BaseLoader.java new file mode 100644 index 000000000..fb7b1809c --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/BaseLoader.java @@ -0,0 +1,153 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.util.Locale; +import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.api.Resource; +import org.apache.velocity.runtime.RuntimeInstance; +import org.apache.velocity.spi.Loader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class BaseLoader implements Loader { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected RuntimeInstance engine; + protected String encoding; + protected Locale locale; + protected boolean reloadable; + protected String[] templateDirectory; // 模板目录 + protected String[] templateSuffix; // 模板后缀 + + @Override + public boolean exists(String name, Locale locale) { + Locale cur = locale; + while (cur != null) { + if (_exists(name, locale, toPath(name, cur))) { + return true; + } + cur = getParentLocale(cur); + } + return _exists(name, locale, toPath(name, null)); + } + + @Override + public Resource load(String name, Locale locale, String encoding) throws IOException { + if (StringUtils.isBlank(encoding)) { + encoding = this.encoding; + } + Locale cur = locale; + String path = toPath(name, cur); + while (cur != null && !_exists(name, locale, path)) { + cur = getParentLocale(cur); + path = toPath(name, cur); + } + return doLoad(name, locale, encoding, path); + } + + protected abstract boolean doExists(String name, Locale locale, String path) throws IOException; + + protected abstract Resource doLoad(String name, Locale locale, String encoding, String path) throws IOException; + + private boolean _exists(String name, Locale locale, String path) { + try { + return doExists(name, locale, path); + } catch (Exception e) { + return false; + } + } + + protected String toPath(String name, Locale locale) { + if (endsWith(name, templateSuffix)) { + name = relocate(name, locale, templateDirectory); + } + return appendLocale(name, locale); + } + + protected String relocate(String name, Locale locale, String[] directories) { + if (directories != null && directories.length > 0) { + for (String directory : directories) { + try { + if (doExists(name, locale, directory + name)) { + return directory + name; + } + } catch (IOException ignore) { + continue; + } + } + return directories[0] + name; + } + return name; + } + + private static boolean endsWith(String value, String[] suffixes) { + if (suffixes == null || suffixes.length == 0) { + return false; + } + for (String suffix : suffixes) { + if (StringUtils.endsWith(value, suffix)) { + return true; + } + } + return false; + } + + private static String appendLocale(String name, Locale locale) { + if (locale == null) { + return name; + } + return locale + name; + } + + private static Locale getParentLocale(Locale locale) { + return null; + } + + // region setter + + public void setEngine(RuntimeInstance engine) { + this.engine = engine; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + + public void setReloadable(boolean reloadable) { + this.reloadable = reloadable; + } + + public void setTemplateDirectory(String[] templateDirectory) { + this.templateDirectory = templateDirectory; + } + + public void setTemplateSuffix(String[] templateSuffix) { + this.templateSuffix = templateSuffix; + } + + // endregion setter + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ClasspathLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ClasspathLoader.java new file mode 100644 index 000000000..30890ac24 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ClasspathLoader.java @@ -0,0 +1,39 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.spi.loaders.resources.ClasspathResource; + +public class ClasspathLoader extends BaseLoader { + + @Override + protected boolean doExists(String name, Locale locale, String path) throws IOException { + return Thread.currentThread().getContextClassLoader().getResource(path) != null; + } + + @Override + protected Resource doLoad(String name, Locale locale, String encoding, String path) throws IOException { + return new ClasspathResource(engine, name, locale, encoding, path); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/DataSourceLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/DataSourceLoader.java new file mode 100644 index 000000000..bb817e7b0 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/DataSourceLoader.java @@ -0,0 +1,39 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.spi.loaders.resources.DataSourceResource; + +public class DataSourceLoader extends BaseLoader { + + @Override + protected boolean doExists(String name, Locale locale, String path) throws IOException { + return false; + } + + @Override + protected Resource doLoad(String name, Locale locale, String encoding, String path) throws IOException { + return new DataSourceResource(engine, name, locale, encoding, 0); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/FileLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/FileLoader.java new file mode 100644 index 000000000..6e04ecf81 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/FileLoader.java @@ -0,0 +1,40 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + + +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.spi.loaders.resources.FileResource; + +public class FileLoader extends BaseLoader { + + @Override + protected boolean doExists(String name, Locale locale, String path) throws IOException { + return false; + } + + @Override + protected Resource doLoad(String name, Locale locale, String encoding, String path) throws IOException { + return new FileResource(engine, name, locale, encoding, path); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/JarLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/JarLoader.java new file mode 100644 index 000000000..3f3ef37e1 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/JarLoader.java @@ -0,0 +1,40 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + + +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.spi.loaders.resources.JarResource; + +public class JarLoader extends BaseLoader { + + @Override + protected boolean doExists(String name, Locale locale, String path) throws IOException { + return false; + } + + @Override + protected Resource doLoad(String name, Locale locale, String encoding, String path) throws IOException { + return new JarResource(engine, name, locale, encoding, path); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/MultiLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/MultiLoader.java new file mode 100644 index 000000000..bc365eb9c --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/MultiLoader.java @@ -0,0 +1,66 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.spi.Loader; + +public class MultiLoader implements Loader { + + private Loader[] loaders; + + @Override + public boolean exists(String name, Locale locale) { + if (loaders.length == 1) { + return loaders[0].exists(name, locale); + } + for (Loader loader : loaders) { + try { + if (loader.exists(name, locale)) { + return true; + } + } catch (Exception ignore) { + } + } + return false; + } + + @Override + public Resource load(String name, Locale locale, String encoding) throws IOException { + for (Loader loader : loaders) { + try { + if (loader.exists(name, locale)) { + return loader.load(name, locale, encoding); + } + } catch (Exception ignore) { + } + } + throw new FileNotFoundException("No such template file: " + name); + } + + public void setLoaders(Loader[] loaders) { + this.loaders = loaders; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/UrlLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/UrlLoader.java new file mode 100644 index 000000000..f2dcba8f7 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/UrlLoader.java @@ -0,0 +1,37 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; + +public class UrlLoader extends BaseLoader { + + @Override + protected boolean doExists(String name, Locale locale, String path) throws IOException { + return false; + } + + @Override + protected Resource doLoad(String name, Locale locale, String encoding, String path) throws IOException { + return null; + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ZipLoader.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ZipLoader.java new file mode 100644 index 000000000..69be77edc --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/ZipLoader.java @@ -0,0 +1,37 @@ +package org.apache.velocity.spi.loaders; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.util.Locale; +import org.apache.velocity.api.Resource; + +public class ZipLoader extends BaseLoader { + + @Override + protected boolean doExists(String name, Locale locale, String path) throws IOException { + return false; + } + + @Override + protected Resource doLoad(String name, Locale locale, String encoding, String path) throws IOException { + return null; + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/BaseResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/BaseResource.java new file mode 100644 index 000000000..13f644abc --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/BaseResource.java @@ -0,0 +1,84 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.Serializable; +import java.util.Locale; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.api.Resource; +import org.apache.velocity.runtime.RuntimeInstance; + +public abstract class BaseResource implements Resource, Serializable { + + private static final long serialVersionUID = 86551207664766539L; + + private final transient RuntimeInstance engine; + private final String name; + private final String encoding; + private final Locale locale; + private final long lastModified; + + public BaseResource(RuntimeInstance engine, String name, Locale locale, String encoding, long lastModified) { + this.engine = engine; + this.name = name; + this.encoding = encoding; + this.locale = locale; + this.lastModified = lastModified; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getEncoding() { + return encoding; + } + + @Override + public Locale getLocale() { + return locale; + } + + @Override + public long getLastModified() { + return lastModified; + } + + @Override + public long getLength() { + return -1; + } + + @Override + public String getSource() throws IOException { + StringBuilder source = new StringBuilder(); + IOUtils.copy(openReader(), source); + return source.toString(); + } + + @Override + public RuntimeInstance getEngine() { + return engine; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ClasspathResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ClasspathResource.java new file mode 100644 index 000000000..7122c22ba --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ClasspathResource.java @@ -0,0 +1,40 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; +import org.apache.velocity.runtime.RuntimeInstance; + +public class ClasspathResource extends InputStreamResource { + + private static final long serialVersionUID = -8216061890256939202L; + + public ClasspathResource(RuntimeInstance engine, String name, Locale locale, String encoding, String file) { + super(engine, name, locale, encoding, file); + } + + @Override + public InputStream openStream() throws IOException { + return Thread.currentThread().getContextClassLoader().getResourceAsStream(getFile()); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/DataSourceResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/DataSourceResource.java new file mode 100644 index 000000000..18955e410 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/DataSourceResource.java @@ -0,0 +1,46 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Locale; +import org.apache.velocity.runtime.RuntimeInstance; + +public class DataSourceResource extends BaseResource { + + private static final long serialVersionUID = 5156212703341663975L; + + public DataSourceResource(RuntimeInstance engine, String name, Locale locale, String encoding, long lastModified) { + super(engine, name, locale, encoding, lastModified); + } + + @Override + public Reader openReader() throws IOException { + return null; + } + + @Override + public InputStream openStream() throws IOException { + return null; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/FileResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/FileResource.java new file mode 100644 index 000000000..018fde6b0 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/FileResource.java @@ -0,0 +1,41 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; +import org.apache.velocity.runtime.RuntimeInstance; + +public class FileResource extends InputStreamResource { + + private static final long serialVersionUID = 3007762548080486248L; + + public FileResource(RuntimeInstance engine, String name, Locale locale, String encoding, String file) { + super(engine, name, locale, encoding, file); + } + + @Override + public InputStream openStream() throws IOException { + return new FileInputStream(getFile()); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/InputStreamResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/InputStreamResource.java new file mode 100644 index 000000000..3b82e2583 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/InputStreamResource.java @@ -0,0 +1,82 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Locale; +import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.runtime.RuntimeInstance; + +public abstract class InputStreamResource extends BaseResource { + + private static final long serialVersionUID = 2729791474763118265L; + + private final String file; + + public InputStreamResource(RuntimeInstance engine, String name, Locale locale, String encoding, String file) { + this(engine, name, locale, encoding, -1, file); + } + + public InputStreamResource(RuntimeInstance engine, String name, Locale locale, String encoding, long lastModified, + String file) { + super(engine, name, locale, encoding, lastModified); + this.file = file; + } + + @Override + public long getLastModified() { + File f = new File(getFile()); + if (f != null && f.exists()) { + return f.lastModified(); + } + return super.getLastModified(); + } + + @Override + public long getLength() { + File f = new File(getFile()); + if (f != null) { + return f.length(); + } + return super.getLength(); + } + + @Override + public Reader openReader() throws IOException { + InputStream in = openStream(); + if (in == null) { + throw new FileNotFoundException( + "Not found template " + getName() + " in " + getClass().getSimpleName() + ": " + file); + } + String encoding = getEncoding(); + return StringUtils.isEmpty(encoding) + ? new InputStreamReader(in) : new InputStreamReader(in, encoding); + } + + public String getFile() { + return file; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/JarResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/JarResource.java new file mode 100644 index 000000000..c52daca5e --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/JarResource.java @@ -0,0 +1,42 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; +import java.util.jar.JarFile; +import org.apache.velocity.runtime.RuntimeInstance; + +public class JarResource extends InputStreamResource { + + private static final long serialVersionUID = 5777154748252092679L; + + public JarResource(RuntimeInstance engine, String name, Locale locale, String encoding, String file) { + super(engine, name, locale, encoding, file); + } + + @Override + public InputStream openStream() throws IOException { + JarFile jarFile = new JarFile(getFile()); + return jarFile.getInputStream(jarFile.getJarEntry(getName())); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/StringResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/StringResource.java new file mode 100644 index 000000000..655284c0a --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/StringResource.java @@ -0,0 +1,57 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.util.Locale; +import org.apache.commons.io.input.ReaderInputStream; +import org.apache.velocity.runtime.RuntimeInstance; + +public class StringResource extends BaseResource { + + private static final long serialVersionUID = -3908373765734849091L; + + private final String source; + + public StringResource(RuntimeInstance engine, String name, Locale locale, String encoding, long lastModified, + String source) { + super(engine, name, locale, encoding, lastModified); + this.source = source; + } + + @Override + public Reader openReader() throws IOException { + return new StringReader(source); + } + + @Override + public InputStream openStream() throws IOException { + return new ReaderInputStream(openReader(), getEncoding()); + } + + @Override + public long getLength() { + return source.length(); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/UrlResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/UrlResource.java new file mode 100644 index 000000000..98ddd898a --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/UrlResource.java @@ -0,0 +1,42 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Locale; +import org.apache.velocity.runtime.RuntimeInstance; + +public class UrlResource extends InputStreamResource { + + private static final long serialVersionUID = 6787846728614581583L; + + public UrlResource(RuntimeInstance engine, String name, Locale locale, String encoding, String file) { + super(engine, name, locale, encoding, file); + } + + @Override + public InputStream openStream() throws IOException { + URL url = new URL(getName()); + return url.openStream(); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ZipResource.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ZipResource.java new file mode 100644 index 000000000..2d4aa3363 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/loaders/resources/ZipResource.java @@ -0,0 +1,41 @@ +package org.apache.velocity.spi.loaders.resources; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; +import java.util.zip.ZipFile; +import org.apache.velocity.runtime.RuntimeInstance; + +public class ZipResource extends InputStreamResource { + + private static final long serialVersionUID = -5614543506378211399L; + + public ZipResource(RuntimeInstance engine, String name, Locale locale, String encoding, String file) { + super(engine, name, locale, encoding, file); + } + + @Override + public InputStream openStream() throws IOException { + ZipFile zipFile = new ZipFile(getFile()); + return zipFile.getInputStream(zipFile.getEntry(getName())); + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/methods/LangMethod.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/methods/LangMethod.java new file mode 100644 index 000000000..e2322970d --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/methods/LangMethod.java @@ -0,0 +1,24 @@ +package org.apache.velocity.spi.methods; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +public class LangMethod { + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/parsers/TemplateParser.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/parsers/TemplateParser.java new file mode 100644 index 000000000..b7c94dc02 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/parsers/TemplateParser.java @@ -0,0 +1,43 @@ +package org.apache.velocity.spi.parsers; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.StringReader; +import org.apache.velocity.Template; +import org.apache.velocity.runtime.parser.ParseException; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.spi.Parser; + +public class TemplateParser implements Parser { + + private org.apache.velocity.runtime.parser.Parser innerParser; + + @Override + public SimpleNode parse(String name, String source) throws ParseException { + Template template = new Template(); + template.setName(name); + return innerParser.parse(new StringReader(source), template); + } + + public void setInnerParser(org.apache.velocity.runtime.parser.Parser innerParser) { + this.innerParser = innerParser; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/CompiledTranslator.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/CompiledTranslator.java new file mode 100644 index 000000000..ecc49618c --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/CompiledTranslator.java @@ -0,0 +1,49 @@ +package org.apache.velocity.spi.translators; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.velocity.api.Resource; +import org.apache.velocity.api.Template; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.spi.Compiler; +import org.apache.velocity.spi.Translator; +import org.apache.velocity.spi.translators.templates.CompiledVisitor; + +public class CompiledTranslator implements Translator { + + private Compiler compiler; + + @Override + public Template translate(Resource resource, SimpleNode root) { + CompiledVisitor visitor = new CompiledVisitor(); + String code = visitor.visit(root, resource); + try { + return (Template) compiler.compile(code).getConstructor().newInstance(); + } catch (Throwable e) { + throw new VelocityException(e); + } + } + + public void setCompiler(Compiler compiler) { + this.compiler = compiler; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/InterpretedTranslator.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/InterpretedTranslator.java new file mode 100644 index 000000000..5681b9d29 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/InterpretedTranslator.java @@ -0,0 +1,35 @@ +package org.apache.velocity.spi.translators; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.velocity.api.Resource; +import org.apache.velocity.api.Template; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.spi.Translator; +import org.apache.velocity.spi.translators.templates.InterpretedTemplate; + +public class InterpretedTranslator implements Translator { + + @Override + public Template translate(Resource resource, SimpleNode root) { + return new InterpretedTemplate(resource, root, null); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/BaseTemplate.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/BaseTemplate.java new file mode 100644 index 000000000..dcd417bbd --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/BaseTemplate.java @@ -0,0 +1,140 @@ +package org.apache.velocity.spi.translators.templates; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.text.ParseException; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.api.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.runtime.RuntimeInstance; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.runtime.visitor.BaseVisitor; + +public abstract class BaseTemplate implements Template { + + private final Resource resource; + private final SimpleNode root; + private final Template parent; + private final String name; + private final String encoding; + private final Locale locale; + private final long lastModified; + private final long length; + + public BaseTemplate(Resource resource, SimpleNode node, Template parent) { + this.resource = resource; + this.root = node; + this.parent = parent; + this.name = resource.getName(); + this.encoding = resource.getEncoding(); + this.locale = resource.getLocale(); + this.lastModified = resource.getLastModified(); + this.length = resource.getLength(); + } + + @Override + public void render(Context context, Writer out) throws IOException, ParseException { + try { + doRender(context, out); + } catch (IOException | ParseException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public Object evaluate(Context context) throws ParseException { + StringWriter writer = new StringWriter(); + try { + render(context, writer); + } catch (IOException e) { + throw new RuntimeException(e); + } + return writer.toString(); + } + + @Override + public void accept(BaseVisitor visitor) { + visitor.visit(root, resource); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getEncoding() { + return encoding; + } + + @Override + public Locale getLocale() { + return locale; + } + + @Override + public long getLastModified() { + return lastModified; + } + + @Override + public long getLength() { + return length; + } + + @Override + public String getSource() throws IOException { + return resource.getSource(); + } + + @Override + public Reader openReader() throws IOException { + return resource.openReader(); + } + + @Override + public InputStream openStream() throws IOException { + return resource.openStream(); + } + + @Override + public RuntimeInstance getEngine() { + return resource.getEngine(); + } + + public SimpleNode getRoot() { + return root; + } + + public Template getParent() { + return parent; + } + + protected abstract void doRender(Context context, Writer out) throws Exception; + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledTemplate.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledTemplate.java new file mode 100644 index 000000000..6e4fe0101 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledTemplate.java @@ -0,0 +1,36 @@ +package org.apache.velocity.spi.translators.templates; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import org.apache.velocity.api.Resource; +import org.apache.velocity.api.Template; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.spi.Compiler; + +public abstract class CompiledTemplate extends BaseTemplate { + + private Compiler compiler; + + public CompiledTemplate(VelocityEngine engine, Resource resource, SimpleNode node, Template parent) { + super(resource, node, parent); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledVisitor.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledVisitor.java new file mode 100644 index 000000000..39f53682b --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/CompiledVisitor.java @@ -0,0 +1,547 @@ +package org.apache.velocity.spi.translators.templates; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.api.Resource; +import org.apache.velocity.api.Template; +import org.apache.velocity.runtime.parser.Token; +import org.apache.velocity.runtime.parser.node.ASTAddNode; +import org.apache.velocity.runtime.parser.node.ASTAndNode; +import org.apache.velocity.runtime.parser.node.ASTAssignment; +import org.apache.velocity.runtime.parser.node.ASTBlock; +import org.apache.velocity.runtime.parser.node.ASTComment; +import org.apache.velocity.runtime.parser.node.ASTDirective; +import org.apache.velocity.runtime.parser.node.ASTDirectiveAssign; +import org.apache.velocity.runtime.parser.node.ASTDivNode; +import org.apache.velocity.runtime.parser.node.ASTEQNode; +import org.apache.velocity.runtime.parser.node.ASTElseIfStatement; +import org.apache.velocity.runtime.parser.node.ASTElseStatement; +import org.apache.velocity.runtime.parser.node.ASTEscape; +import org.apache.velocity.runtime.parser.node.ASTEscapedDirective; +import org.apache.velocity.runtime.parser.node.ASTExpression; +import org.apache.velocity.runtime.parser.node.ASTFalse; +import org.apache.velocity.runtime.parser.node.ASTFloatingPointLiteral; +import org.apache.velocity.runtime.parser.node.ASTGENode; +import org.apache.velocity.runtime.parser.node.ASTGTNode; +import org.apache.velocity.runtime.parser.node.ASTIdentifier; +import org.apache.velocity.runtime.parser.node.ASTIfStatement; +import org.apache.velocity.runtime.parser.node.ASTIncludeStatement; +import org.apache.velocity.runtime.parser.node.ASTIndex; +import org.apache.velocity.runtime.parser.node.ASTIntegerLiteral; +import org.apache.velocity.runtime.parser.node.ASTIntegerRange; +import org.apache.velocity.runtime.parser.node.ASTLENode; +import org.apache.velocity.runtime.parser.node.ASTLTNode; +import org.apache.velocity.runtime.parser.node.ASTMap; +import org.apache.velocity.runtime.parser.node.ASTMethod; +import org.apache.velocity.runtime.parser.node.ASTModNode; +import org.apache.velocity.runtime.parser.node.ASTMulNode; +import org.apache.velocity.runtime.parser.node.ASTNENode; +import org.apache.velocity.runtime.parser.node.ASTNegateNode; +import org.apache.velocity.runtime.parser.node.ASTNotNode; +import org.apache.velocity.runtime.parser.node.ASTObjectArray; +import org.apache.velocity.runtime.parser.node.ASTOrNode; +import org.apache.velocity.runtime.parser.node.ASTParameters; +import org.apache.velocity.runtime.parser.node.ASTReference; +import org.apache.velocity.runtime.parser.node.ASTSetDirective; +import org.apache.velocity.runtime.parser.node.ASTStringLiteral; +import org.apache.velocity.runtime.parser.node.ASTSubtractNode; +import org.apache.velocity.runtime.parser.node.ASTText; +import org.apache.velocity.runtime.parser.node.ASTTextblock; +import org.apache.velocity.runtime.parser.node.ASTTrue; +import org.apache.velocity.runtime.parser.node.ASTVariable; +import org.apache.velocity.runtime.parser.node.ASTWord; +import org.apache.velocity.runtime.parser.node.ASTprocess; +import org.apache.velocity.runtime.parser.node.Node; +import org.apache.velocity.runtime.parser.node.NodeUtils; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.runtime.visitor.BaseVisitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CompiledVisitor extends BaseVisitor { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final StringBuilder codeBuilder = new StringBuilder(1024 * 8); + + private String[] staticImportPackages = new String[]{"org.apache.velocity.spi.methods.LangMethod"}; + private String[] importPackages; + private int ident = 2; + + @Override + public Object visit(ASTAssignment node, Object data) { + return null; + } + + @Override + public Object visit(ASTComment node, Object data) { + return ""; + } + + @Override + public Object visit(ASTDirective node, Object data) { + // TODO: 2021/11/23 + return ""; + } + + @Override + public Object visit(ASTDirectiveAssign node, Object data) { + return null; + } + + @Override + public Object visit(ASTSetDirective node, Object data) { + // https://velocity.apache.org/engine/1.7/user-guide.html#set + // lhs: + // 1. a variable reference + // 2. a property reference + // rhs: + // 1. Variable reference + // 2. String literal + // 3. Property reference + // 4. Method reference + // 5. Number literal + // 6. ArrayList + // 7. Map + // #set( $monkey = $bill ) ## variable reference + // #set( $monkey.Friend = "monica" ) ## string literal + // #set( $monkey.Blame = $hello.Leak ) ## property reference + // #set( $monkey.Plan = $hello.weave($web) ) ## method reference + // #set( $monkey.Number = 123 ) ## number literal + // #set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList + // #set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"}) ## Map + + String key = (String) dispatch(node.jjtGetChild(0), (Resource) data); + Object val = dispatch(node.jjtGetChild(1), (Resource) data); + return "$context.put(\"" + key + "\"," + val + ");"; + } + + @Override + public String visit(ASTExpression node, Object data) { + return (String) dispatch(node.jjtGetChild(0), (Resource) data); + } + + @Override + public String visit(ASTReference node, Object data) { + // There are three types of references in the VTL: variables, properties and methods. + // https://velocity.apache.org/engine/1.7/user-guide.html#references + // 1. Variables + // 2. Properties + // 3. Methods + return null; + } + + @Override + public Object visit(ASTBlock node, Object data) { + int k = node.jjtGetNumChildren(); + Resource resource = (Resource) data; + StringBuilder block = new StringBuilder(); + for (int i = 0; i < k; i++) { + block.append(dispatch(node.jjtGetChild(i), resource)).append("\r\n"); + } + return block.toString().trim(); + } + + // region if/elseif/else + + @Override + public Object visit(ASTIfStatement node, Object data) { + /* + * if () { // child:0 + * xx // child:1 + * } else if () { // child:2 + * yy // + * } else { + * zz // child:3 + * } + */ + int k = node.jjtGetNumChildren(); + Resource resource = (Resource) data; + String cond = (String) dispatch(node.jjtGetChild(0), resource); + String then = (String) dispatch(node.jjtGetChild(1), resource); + StringBuilder ifStmt = new StringBuilder(); + ifStmt.append(append(ident, "if (")).append(cond).append(") {\r\n"); + ifStmt.append(append(ident + 2, then)).append("\r\n"); + ifStmt.append(append(ident, "}")); + if (k > 2) { + // else if + for (int i = 2; i < k; i++) { + ifStmt.append(dispatch(node.jjtGetChild(i), resource)); + } + } + return ifStmt.toString(); + } + + @Override + public Object visit(ASTElseIfStatement node, Object data) { + String cond = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String then = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return append(1, "else if (") + cond + ") {\n" + + append(ident + 2, then) + "\r\n" + + append(ident, "}"); + } + + @Override + public Object visit(ASTElseStatement node, Object data) { + String then = (String) dispatch(node.jjtGetChild(0), (Resource) data); + return append(1, "else {\r\n") + + append(ident + 2, then) + "\r\n" + + append(ident, "}"); + } + + // endregion if/elseif/else + + // region binaryOperator + + // region comparison + + @Override + public String visit(ASTNENode node, Object data) { + Object lhs = dispatch(node.jjtGetChild(0), (Resource) data); + Object rhs = dispatch(node.jjtGetChild(1), (Resource) data); + return "!java.util.Objects.equals(" + lhs + ", " + rhs + ")"; + } + + @Override + public String visit(ASTEQNode node, Object data) { + Object lhs = dispatch(node.jjtGetChild(0), (Resource) data); + Object rhs = dispatch(node.jjtGetChild(1), (Resource) data); + return "java.util.Objects.equals(" + lhs + ", " + rhs + ")"; + } + + @Override + public String visit(ASTGENode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " >= " + rhs; + } + + @Override + public String visit(ASTGTNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " > " + rhs; + } + + @Override + public String visit(ASTLENode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " <= " + rhs; + } + + @Override + public String visit(ASTLTNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " < " + rhs; + } + + // endregion comparison + + // region logical + + @Override + public Object visit(ASTAndNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " && " + rhs; + } + + @Override + public Object visit(ASTOrNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " || " + rhs; + } + + @Override + public Object visit(ASTNotNode node, Object data) { + String mhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + return "!" + mhs; + } + + // endregion logical + + // region math + + @Override + public Object visit(ASTAddNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " + " + rhs; + } + + @Override + public Object visit(ASTDivNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " / " + rhs; + } + + @Override + public Object visit(ASTModNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " % " + rhs; + } + + @Override + public Object visit(ASTMulNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " * " + rhs; + } + + @Override + public Object visit(ASTSubtractNode node, Object data) { + String lhs = (String) dispatch(node.jjtGetChild(0), (Resource) data); + String rhs = (String) dispatch(node.jjtGetChild(1), (Resource) data); + return lhs + " - " + rhs; + } + + // endregion math + + // endregion binaryOperator + + // region literal + + @Override + public String visit(ASTStringLiteral node, Object data) { + return "\"" + node.getFirstToken().image.substring(1, node.getFirstToken().image.length() - 1) + "\""; + } + + @Override + public BigInteger visit(ASTIntegerLiteral node, Object data) { + return new BigInteger(node.getFirstToken().image); + } + + @Override + public BigDecimal visit(ASTFloatingPointLiteral node, Object data) { + return new BigDecimal(node.getFirstToken().image); + } + + // endregion literal + + @Override + public String visit(ASTText node, Object data) { + Token t = node.getFirstToken(); + List text = new ArrayList<>(); + for (; t != node.getLastToken(); t = t.next) { + text.add(StringUtils.trim(NodeUtils.tokenLiteral(node.getParser(), t))); + } + text.add(StringUtils.trim(NodeUtils.tokenLiteral(node.getParser(), t))); + return text.stream() + .filter(StringUtils::isNotBlank) + .map(s -> " $output.write(\"" + s + "\");") + .collect(Collectors.joining("\r\n")); + } + + /** + *

+   * {@code
+   *    package xxx;
+   *
+   *    import yyy.y.y.y;
+   *    import zzz.z.z.z;
+   *
+   *    public final class Template_$name_$locale_$encoding extends Template {
+   *
+   *      private final VelocityEngine engine;
+   *
+   *      @Override
+   *      public boolean render(Context $context, Writer $writer) throws Exception {
+   *        writer.write(xx)
+   *        return true;
+   *      }
+   *    }
+   *
+   * }
+   * 
+ * + * @return Template + */ + @Override + public String visit(SimpleNode node, Object data) { + int i, k = node.jjtGetNumChildren(); + Resource resource = (Resource) data; + StringBuilder sourceCode = new StringBuilder(); + StringBuilder methodCode = new StringBuilder(); + + for (i = 0; i < k; i++) { + methodCode.append(dispatch(node.jjtGetChild(i), resource)).append("\r\n"); + } + + String pkgName = CompiledTemplate.class.getPackage().getName(); + String clsName = getTemplateClassName(resource); + + methodCode.append(this.codeBuilder); + sourceCode.append("package ").append(pkgName).append(";\r\n\r\n"); + Arrays.stream(staticImportPackages).map(s -> "import static " + s + ".*;\r\n").forEach(sourceCode::append); + sourceCode.append("\r\n"); + sourceCode.append("public final class ").append(clsName).append(" extends CompiledTemplate {\r\n\r\n"); + sourceCode.append(" @Override\r\n"); + sourceCode.append(" protected void doRender(Context $context, Writer $output) throws Exception {\r\n"); + sourceCode.append(" ").append("").append("\r\n"); + sourceCode.append(" }\r\n"); + sourceCode.append("}\r\n"); + String clazz = sourceCode.toString(); + logger.debug(clazz); + return clazz; + } + + private Object dispatch(Node node, Resource resource) { + if (node instanceof ASTAssignment) { + return visit((ASTAssignment) node, resource); + } + // region BinaryOperator Comparison + else if (node instanceof ASTNENode) { + return visit((ASTNENode) node, resource); + } else if (node instanceof ASTEQNode) { + return visit((ASTEQNode) node, resource); + } else if (node instanceof ASTGENode) { + return visit((ASTGENode) node, resource); + } else if (node instanceof ASTGTNode) { + return visit((ASTGTNode) node, resource); + } else if (node instanceof ASTLENode) { + return visit((ASTLENode) node, resource); + } else if (node instanceof ASTLTNode) { + return visit((ASTLTNode) node, resource); + } + // endregion BinaryOperator Comparison + + // region BinaryOperator Logical + else if (node instanceof ASTAndNode) { + return visit((ASTAndNode) node, resource); + } else if (node instanceof ASTOrNode) { + return visit((ASTOrNode) node, resource); + } + // endregion BinaryOperator Logical + + // region BinaryOperator Math + else if (node instanceof ASTAddNode) { + return visit((ASTAddNode) node, resource); + } else if (node instanceof ASTDivNode) { + return visit((ASTDivNode) node, resource); + } else if (node instanceof ASTModNode) { + return visit((ASTModNode) node, resource); + } else if (node instanceof ASTMulNode) { + return visit((ASTMulNode) node, resource); + } else if (node instanceof ASTSubtractNode) { + return visit((ASTSubtractNode) node, resource); + } + // endregion BinaryOperator Math + + else if (node instanceof ASTBlock) { + return visit((ASTBlock) node, resource); + } else if (node instanceof ASTComment) { + return visit((ASTComment) node, resource); + } else if (node instanceof ASTDirective) { + return visit((ASTDirective) node, resource); + } else if (node instanceof ASTDirectiveAssign) { + return visit((ASTDirectiveAssign) node, resource); + } else if (node instanceof ASTElseIfStatement) { + return visit((ASTElseIfStatement) node, resource); + } else if (node instanceof ASTElseStatement) { + return visit((ASTElseStatement) node, resource); + } else if (node instanceof ASTEscape) { + return visit((ASTEscape) node, resource); + } else if (node instanceof ASTEscapedDirective) { + return visit((ASTEscapedDirective) node, resource); + } else if (node instanceof ASTExpression) { + return visit((ASTExpression) node, resource); + } else if (node instanceof ASTFalse) { + return visit((ASTFalse) node, resource); + } else if (node instanceof ASTFloatingPointLiteral) { + return visit((ASTFloatingPointLiteral) node, resource); + } else if (node instanceof ASTIntegerLiteral) { + return visit((ASTIntegerLiteral) node, resource); + } else if (node instanceof ASTStringLiteral) { + return visit((ASTStringLiteral) node, resource); + } else if (node instanceof ASTIdentifier) { + return visit((ASTIdentifier) node, resource); + } else if (node instanceof ASTIfStatement) { + return visit((ASTIfStatement) node, resource); + } else if (node instanceof ASTIncludeStatement) { + return visit((ASTIncludeStatement) node, resource); + } else if (node instanceof ASTIndex) { + return visit((ASTIndex) node, resource); + } else if (node instanceof ASTIntegerRange) { + return visit((ASTIntegerRange) node, resource); + } else if (node instanceof ASTMap) { + return visit((ASTMap) node, resource); + } else if (node instanceof ASTMethod) { + return visit((ASTMethod) node, resource); + } else if (node instanceof ASTNegateNode) { + return visit((ASTNegateNode) node, resource); + } else if (node instanceof ASTNotNode) { + return visit((ASTNotNode) node, resource); + } else if (node instanceof ASTObjectArray) { + return visit((ASTObjectArray) node, resource); + } else if (node instanceof ASTParameters) { + return visit((ASTParameters) node, resource); + } else if (node instanceof ASTprocess) { + return visit((ASTprocess) node, resource); + } else if (node instanceof ASTReference) { + return visit((ASTReference) node, resource); + } else if (node instanceof ASTSetDirective) { + return visit((ASTSetDirective) node, resource); + } else if (node instanceof ASTText) { + return visit((ASTText) node, resource); + } else if (node instanceof ASTTextblock) { + return visit((ASTTextblock) node, resource); + } else if (node instanceof ASTTrue) { + return visit((ASTTrue) node, resource); + } else if (node instanceof ASTVariable) { + return visit((ASTVariable) node, resource); + } else if (node instanceof ASTWord) { + return visit((ASTWord) node, resource); + } else { + throw new IllegalArgumentException("unknown node:" + node); + } + } + + private String getTemplateClassName(Resource resource) { + // Template_name_locale_encoding + StringBuilder name = new StringBuilder(); + name.append(Template.class.getSimpleName()); + name.append("_"); + name.append(resource.getName()); + if (resource.getLocale() != null) { + name.append("_"); + name.append(resource.getLocale()); + } + if (StringUtils.isNotBlank(resource.getEncoding())) { + name.append("_"); + name.append(resource.getEncoding()); + } + return name.toString().replaceAll("[-.]", "_"); + } + + private String append(int ident, String s) { + return StringUtils.repeat(' ', ident) + s; + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedTemplate.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedTemplate.java new file mode 100644 index 000000000..f67e18836 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedTemplate.java @@ -0,0 +1,47 @@ +package org.apache.velocity.spi.translators.templates; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.Writer; +import org.apache.velocity.api.Resource; +import org.apache.velocity.api.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.context.InternalContextAdapterImpl; +import org.apache.velocity.runtime.parser.node.SimpleNode; + +public class InterpretedTemplate extends BaseTemplate { + + public InterpretedTemplate(Resource resource, SimpleNode node, Template parent) { + super(resource, node, parent); + } + + @Override + protected void doRender(Context context, Writer out) throws Exception { + InternalContextAdapterImpl ctx = new InternalContextAdapterImpl(context); + InterpretedVisitor visitor = new InterpretedVisitor(); + visitor.setWriter(out); + visitor.setContext(ctx); + + getRoot().init(ctx, getEngine()); + + accept(visitor); + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedVisitor.java b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedVisitor.java new file mode 100644 index 000000000..1a07c422d --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/spi/translators/templates/InterpretedVisitor.java @@ -0,0 +1,37 @@ +package org.apache.velocity.spi.translators.templates; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.IOException; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.parser.node.SimpleNode; +import org.apache.velocity.runtime.visitor.BaseVisitor; + +public class InterpretedVisitor extends BaseVisitor { + + @Override + public Object visit(SimpleNode node, Object data) { + try { + return node.render(context, writer); + } catch (IOException e) { + throw new VelocityException(e); + } + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java index 34ddf5564..175bc9361 100644 --- a/velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java +++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/ClassUtils.java @@ -19,6 +19,9 @@ * under the License. */ +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; import org.apache.velocity.context.InternalContextAdapter; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.VelocityException; @@ -28,8 +31,6 @@ import org.apache.velocity.util.introspection.IntrospectionCacheData; import org.apache.velocity.util.introspection.VelMethod; -import java.io.InputStream; - /** @@ -42,6 +43,9 @@ */ public class ClassUtils { + public static final String CLASS_EXTENSION = ".class"; + public static final String JAVA_EXTENSION = ".java"; + /** * Utility class; cannot be instantiated. */ @@ -268,4 +272,11 @@ public static VelMethod getMethod(String methodName, Object[] params, return method; } + public static URI toURI(String name) { + try { + return new URI(name); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } } diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/util/DeprecationAwareExtProperties.java b/velocity-engine-core/src/main/java/org/apache/velocity/util/DeprecationAwareExtProperties.java index a67454a5d..bccf36310 100644 --- a/velocity-engine-core/src/main/java/org/apache/velocity/util/DeprecationAwareExtProperties.java +++ b/velocity-engine-core/src/main/java/org/apache/velocity/util/DeprecationAwareExtProperties.java @@ -103,7 +103,8 @@ protected String translateKey(String key) */ public Object get(String key) { - return super.get(translateKey(key)); + key = translateKey(key); + return super.get(key); } /** diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavacCompilerTest.java b/velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavacCompilerTest.java new file mode 100644 index 000000000..b1f8dd0de --- /dev/null +++ b/velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavacCompilerTest.java @@ -0,0 +1,12 @@ +package org.apache.velocity.spi.compilers; + +import org.junit.Test; + +public class JavacCompilerTest { + + @Test + public void test_compile() { + JavacCompiler compiler = new JavacCompiler(); + } + +} diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavassistCompilerTest.java b/velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavassistCompilerTest.java new file mode 100644 index 000000000..e07da608a --- /dev/null +++ b/velocity-engine-core/src/test/java/org/apache/velocity/spi/compilers/JavassistCompilerTest.java @@ -0,0 +1,12 @@ +package org.apache.velocity.spi.compilers; + +import org.junit.Test; + +public class JavassistCompilerTest { + + @Test + public void test_compile() { + JavacCompiler compiler = new JavacCompiler(); + } + +} diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/spi/loaders/ClasspathLoaderTest.java b/velocity-engine-core/src/test/java/org/apache/velocity/spi/loaders/ClasspathLoaderTest.java new file mode 100644 index 000000000..b6d2b121e --- /dev/null +++ b/velocity-engine-core/src/test/java/org/apache/velocity/spi/loaders/ClasspathLoaderTest.java @@ -0,0 +1,32 @@ +package org.apache.velocity.spi.loaders; + +import java.io.IOException; +import java.io.Reader; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.runtime.RuntimeInstance; +import org.junit.Assert; +import org.junit.Test; + +public class ClasspathLoaderTest { + + @Test + public void test_load() { + ClasspathLoader loader = new ClasspathLoader(); + RuntimeInstance runtimeInstance = new RuntimeInstance(); + runtimeInstance.init(); + loader.setEngine(runtimeInstance); + loader.setEncoding("utf-8"); + loader.setLocale(Locale.getDefault()); + loader.setReloadable(true); + loader.setTemplateSuffix(new String[]{".vm"}); + loader.setTemplateDirectory(new String[]{"evaluate/"}); + try { + Resource resource = loader.load("eval1.vm", null, null); + try (Reader reader = resource.openReader()) { + } + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + } +} diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/spi/translators/CompiledTranslatorTest.java b/velocity-engine-core/src/test/java/org/apache/velocity/spi/translators/CompiledTranslatorTest.java new file mode 100644 index 000000000..8285f037a --- /dev/null +++ b/velocity-engine-core/src/test/java/org/apache/velocity/spi/translators/CompiledTranslatorTest.java @@ -0,0 +1,62 @@ +package org.apache.velocity.spi.translators; + +import java.io.IOException; +import java.io.Reader; +import java.util.Locale; +import org.apache.velocity.api.Resource; +import org.apache.velocity.runtime.RuntimeInstance; +import org.apache.velocity.runtime.parser.ParseException; +import org.apache.velocity.spi.Loader; +import org.apache.velocity.spi.Parser; +import org.apache.velocity.spi.compilers.JavassistCompiler; +import org.apache.velocity.spi.loaders.ClasspathLoader; +import org.apache.velocity.spi.parsers.TemplateParser; +import org.junit.Assert; +import org.junit.Test; + +public class CompiledTranslatorTest { + + @Test + public void test_translate() { + RuntimeInstance runtimeInstance = new RuntimeInstance(); + runtimeInstance.init(); + Loader loader = creatLoader(runtimeInstance); + Parser parser = createParser(runtimeInstance); + + CompiledTranslator translator = new CompiledTranslator(); + translator.setCompiler(new JavassistCompiler()); + Resource resource; + String res; + try { + resource = loader.load("eval1.vm", null, null); + try (Reader reader = resource.openReader()) { + StringBuilder out = new StringBuilder(); + int ch; + while ((ch = reader.read()) != -1) { + out.append((char) ch); + } + res = out.toString(); + } + translator.translate(resource, parser.parse("eval1.vm", res)); + } catch (IOException | ParseException e) { + Assert.fail(e.getMessage()); + } + } + + private Parser createParser(RuntimeInstance runtimeInstance) { + TemplateParser parser = new TemplateParser(); + parser.setInnerParser(runtimeInstance.createNewParser()); + return parser; + } + + private Loader creatLoader(RuntimeInstance runtimeInstance) { + ClasspathLoader loader = new ClasspathLoader(); + loader.setEngine(runtimeInstance); + loader.setEncoding("utf-8"); + loader.setLocale(Locale.getDefault()); + loader.setReloadable(true); + loader.setTemplateSuffix(new String[]{".vm"}); + loader.setTemplateDirectory(new String[]{"evaluate/"}); + return loader; + } +}