/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jctools.util; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.util.List; /** * A single class templating library for doing runtime code-gen. * * Not Threadsafe. */ public class Template { private final String template; private int index; private int previousIndex; public static Template fromFile(final Class<?> resourceRoot, final String fileName) { InputStream templateStream = resourceRoot.getResourceAsStream(fileName); if(templateStream == null) { throw new IllegalArgumentException("Template file of the name: \'"+fileName+"\' was not found."); } return fromStream(templateStream); } private static Template fromStream(InputStream templateStream) { if(templateStream == null) { throw new IllegalArgumentException("Null template stream"); } BufferedReader reader = new BufferedReader(new InputStreamReader(templateStream)); try { try { StringBuilder buffer = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { buffer.append(line); buffer.append('\n'); } return new Template(buffer.toString()); } finally { reader.close(); } } catch (IOException e) { throw new IllegalArgumentException(e); } } public Template(final String template) { this.template = template; } public String render(Object o) { StringBuilder result = new StringBuilder(template.length()); render(o, result); return result.toString(); } private void render(Object obj, StringBuilder result) { render(obj, result, false); } private void render(Object obj, StringBuilder result, boolean last) { index = 0; while(scanNextTag()) { copyPrefixTo(result); index += 2; if (isLoopTag()) { index++; String tagName = readTagName(); Template body = extractLoopBody(tagName); List<?> values = (List<?>) readTagValue(tagName, obj, last); int lastIndex = values.size() - 1; for (int i = 0; i < values.size(); i++) { body.render(values.get(i), result, i == lastIndex); } } else { String tagName = readTagName(); result.append(readTagValue(tagName, obj, false)); } } copySuffixTo(result); } private Template extractLoopBody(String tagName) { String closingTag = "{{/" + tagName + "}}"; int endOfBody = template.indexOf(closingTag, index); Template body = new Template(template.substring(index, endOfBody)); index = endOfBody + closingTag.length(); return body; } private boolean isLoopTag() { return template.charAt(index) == '#'; } private boolean scanNextTag() { previousIndex = index; index = template.indexOf("{{", index); return index != -1; } private void copyPrefixTo(StringBuilder result) { result.append(template, previousIndex, index); } private Object readTagValue(String tagName, Object obj, boolean last) { Object value = readBuiltinTag(tagName, obj, last); if (value != null) return value; return readField(tagName, obj); } private Object readBuiltinTag(String tagName, Object obj, boolean last) { if ("notLast".equals(tagName)) { if (last) { return emptyList(); } else { return singletonList(obj); } } return null; } private Object readField(String tagName, Object obj) { Class<?> cls = obj.getClass(); try { return cls.getField(tagName) .get(obj); } catch (NoSuchFieldException ignored) { try { return cls.getMethod(tagName) .invoke(obj); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e); } catch (InvocationTargetException e) { throw new IllegalArgumentException(e); } catch (IllegalAccessException e) { throw new IllegalArgumentException(e); } } catch (IllegalAccessException e) { throw new IllegalArgumentException(e); } } private String readTagName() { int endTagIndex = template.indexOf("}}", index); String tagName = template.substring(index, endTagIndex); index = endTagIndex + 2; return tagName; } private void copySuffixTo(StringBuilder result) { // Copy from previousIndex because index will be -1 at this point result.append(template, previousIndex, template.length()); } }