001: /******************************************************************************
002: * JBoss, a division of Red Hat *
003: * Copyright 2006, Red Hat Middleware, LLC, and individual *
004: * contributors as indicated by the @authors tag. See the *
005: * copyright.txt in the distribution for a full listing of *
006: * individual contributors. *
007: * *
008: * This is free software; you can redistribute it and/or modify it *
009: * under the terms of the GNU Lesser General Public License as *
010: * published by the Free Software Foundation; either version 2.1 of *
011: * the License, or (at your option) any later version. *
012: * *
013: * This software is distributed in the hope that it will be useful, *
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of *
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
016: * Lesser General Public License for more details. *
017: * *
018: * You should have received a copy of the GNU Lesser General Public *
019: * License along with this software; if not, write to the Free *
020: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
021: * 02110-1301 USA, or see the FSF site: http://www.fsf.org. *
022: ******************************************************************************/package org.jboss.portal.format.template;
023:
024: import javassist.CannotCompileException;
025: import javassist.ClassPool;
026: import javassist.CtClass;
027: import javassist.CtConstructor;
028: import javassist.CtField;
029: import javassist.CtMethod;
030: import javassist.CtNewMethod;
031: import javassist.LoaderClassPath;
032: import javassist.Modifier;
033: import javassist.NotFoundException;
034: import org.apache.log4j.Logger;
035: import org.dom4j.Document;
036: import org.dom4j.Element;
037: import org.dom4j.Node;
038: import org.dom4j.Text;
039:
040: import java.io.Writer;
041: import java.util.Iterator;
042: import java.util.LinkedList;
043: import java.util.Set;
044:
045: /** @author <a href="mailto:julien@jboss.org">Julien Viet</a> */
046: public class TemplateBuilder {
047:
048: private static final String CONTEXT = Context.class.getName();
049: private static final String ITERATOR = Iterator.class.getName();
050:
051: private final ClassPool pool;
052: private final Element node;
053: private final int index;
054: private final Logger log;
055: private final Set propertyNames;
056: private CtClass cc;
057:
058: public static int classNameCounter = 0;
059:
060: public TemplateBuilder(Document doc, Set propertyNames) {
061: this (doc.getRootElement(), propertyNames);
062: }
063:
064: public TemplateBuilder(Element node, Set propertyNames) {
065: this (new ClassPool(null), node, propertyNames);
066: pool.appendClassPath(new LoaderClassPath(Thread.currentThread()
067: .getContextClassLoader()));
068: }
069:
070: public TemplateBuilder(ClassPool pool, Document doc, Set names) {
071: this (pool, doc.getRootElement(), names);
072: }
073:
074: public TemplateBuilder(ClassPool pool, Element node, Set names) {
075: this .index = classNameCounter++;
076: this .pool = pool;
077: this .node = node;
078: this .propertyNames = names;
079: this .log = Logger.getLogger(getClass() + "." + index);
080: }
081:
082: public CtClass getGeneratedClass() {
083: return cc;
084: }
085:
086: public void build() throws BuildException {
087: try {
088: cc = pool
089: .makeClass("org.jboss.nukes.common.template.Generated"
090: + index);
091: cc.setSuperclass(pool.get(Template.class.getName()));
092: cc.setModifiers(Modifier.PUBLIC);
093:
094: addConstructor();
095: implementRender();
096: } catch (CannotCompileException e) {
097: throw new BuildException(
098: "Unexpected error, cannot build template", e);
099: } catch (NotFoundException e) {
100: throw new BuildException(
101: "Unexpected error, cannot build template", e);
102: }
103: }
104:
105: private void addConstructor() throws CannotCompileException,
106: NotFoundException {
107: CtConstructor ctor = new CtConstructor(new CtClass[] { pool
108: .get(CONTEXT) }, cc);
109: ctor.setBody("{ super($1); }");
110: cc.addConstructor(ctor);
111: log.debug("Added constructor on template " + cc.getName());
112: }
113:
114: private void implementRender() throws CannotCompileException,
115: NotFoundException {
116: LinkedList stack = new LinkedList();
117: stack.add("");
118: StringBuffer buffer = new StringBuffer();
119: buffer.append("public void render(").append(
120: Context.class.getName()).append(" ctx, ").append(
121: Writer.class.getName()).append(" writer)\n").append(
122: "{\n").append(" ").append(CONTEXT).append(
123: " ctx_ = $1;\n");
124: generateRender(stack, buffer, " ", node);
125: buffer.append("}\n");
126: String code = buffer.toString();
127: CtMethod method = CtNewMethod.make(code, cc);
128: cc.addMethod(method);
129: CtField gc = new CtField(pool.get(String.class.getName()),
130: "_generated_code", cc);
131: gc.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
132: CtField.Initializer initializer = CtField.Initializer
133: .constant(code);
134: cc.addField(gc, initializer);
135: log.debug("Created render method on template " + cc.getName()
136: + " with body " + code);
137: }
138:
139: private void generateRender(LinkedList stack, StringBuffer buffer,
140: String indent, Element node) {
141: buffer.append(indent).append("String s_").append(stack.size())
142: .append(" = null;\n");
143: for (Iterator i = node.content().iterator(); i.hasNext();) {
144: Node item = (Node) i.next();
145: if (item instanceof Text) {
146: String text = item.getText().trim();
147: text = org.jboss.portal.common.util.Tools.replace(text,
148: "\n", "\\n");
149: text = org.jboss.portal.common.util.Tools.replace(text,
150: "\"", "\\\"");
151: buffer.append(indent).append("$2.write(\"")
152: .append(text).append("\");\n");
153: } else if (item instanceof Element) {
154: Element elt = (Element) item;
155: if ("loop".equals(elt.getName())) {
156: String name = elt.attributeValue("name");
157: String iterator = "it_" + name;
158: String context = "ctx_" + name;
159: buffer.append(indent).append("for (").append(
160: ITERATOR).append(" ").append(iterator)
161: .append(" = ").append("ctx_").append(
162: stack.getLast()).append(
163: ".childIterator(\"").append(name)
164: .append("\");").append(iterator).append(
165: ".hasNext();").append(")\n")
166: .append(indent).append("{\n")
167: .append(indent).append(" ").append(
168: CONTEXT).append(" ")
169: .append(context).append(" = (").append(
170: CONTEXT).append(")").append(
171: iterator).append(".next();\n");
172: stack.add(name);
173: generateRender(stack, buffer, indent + " ", elt
174: .element("node"));
175: stack.removeLast();
176: buffer.append(indent).append("}\n");
177: } else if ("ref".equals(elt.getName())) {
178: String name = elt.attributeValue("name");
179: int depth = Integer.parseInt(elt
180: .attributeValue("depth"));
181: String target = null;
182: if (depth == 0) {
183: target = propertyNames.contains(name) ? "$0.local"
184: : "ctx_";
185: } else {
186: target = "ctx_" + (String) stack.get(depth);
187: }
188: if (target != null) {
189: buffer
190: .append(indent)
191: .append("s_")
192: .append(stack.size())
193: .append(" = (java.lang.String)")
194: .append(target)
195: .append(".get(\"")
196: .append(name)
197: .append("\");\n")
198: .append(indent)
199: .append("if (s_")
200: .append(stack.size())
201: .append(" != null)\n")
202: .append(indent)
203: .append("{\n")
204: .append(indent)
205: .append(" $2.write(s_")
206: .append(stack.size())
207: .append(");\n")
208: .append(indent)
209: .append("}\n")
210: .append(indent)
211: .append(
212: "else if (log.isDebugEnabled())\n")
213: .append(indent).append("{\n").append(
214: indent).append(
215: " log.debug(\"field ")
216: .append(name).append(" is null\");\n")
217: .append(indent).append("}\n");
218: } else {
219: // log.warn("Path not usable ");
220: }
221: }
222: }
223: }
224: }
225:
226: }
|