1 package net.sf.jstp;
2
3 import java.io.*;
4 import java.util.*;
5
6 public class JstpTranslator
7 {
8 static public void translate(String pkgName, String className, Reader input, Writer output) throws IOException, JstpParserException
9 {
10 String RENDER_METHOD = "public void render(java.io.PrintWriter out)";
11 String TEXT_WRAPPER = "out.print";
12 String EXPR_WRAPPER = "String.valueOf";
13 String INDENTATION = " ";
14 boolean WS_GOBBLING = true;
15
16 PrintWriter out = (output instanceof PrintWriter)? (PrintWriter)output : new PrintWriter(output);
17
18 JstpParser parser = new JstpParser(input);
19 parser.parse();
20
21 String superClassName = null;
22 String importList = null;
23 List declarations = new ArrayList();
24 List renderSegments = new ArrayList();
25
26 for(int i=0; i<parser.elements.size(); i++)
27 {
28 JstpElement elem = (JstpElement)parser.elements.get(i);
29 switch(elem.type)
30 {
31 case JstpElement.TYPE_COMMENT :
32
33 break;
34
35 case JstpElement.TYPE_DECLARATION :
36 declarations.add(elem);
37 break;
38
39 case JstpElement.TYPE_SCRIPTLET :
40 renderSegments.add(elem);
41 break;
42
43 case JstpElement.TYPE_EXPRESSION :
44 if(isBlank(elem.content))
45 throw new JstpParserException("empty expression", elem.row, elem.col);
46 renderSegments.add(elem);
47 break;
48
49 case JstpElement.TYPE_TEXT :
50 renderSegments.add(elem);
51 break;
52
53 case JstpElement.TYPE_DIRECTIVE :
54 if(!elem.directiveName.equals("page"))
55 throw new JstpParserException("directive "+elem.directiveName+" not supported", elem.row, elem.col);
56 for(Iterator ater=elem.directiveAttributes.keySet().iterator(); ater.hasNext();)
57 {
58 String attrName = (String)ater.next();
59 String attrValue = (String)elem.directiveAttributes.get(attrName);
60 if(attrName.equals("package"))
61 pkgName = attrValue;
62 else if(attrName.equals("extends"))
63 superClassName = attrValue;
64 else if(attrName.equals("import"))
65 importList = attrValue;
66 else if(attrName.equals("render-method"))
67 RENDER_METHOD = attrValue;
68 else if(attrName.equals("text-wrapper"))
69 TEXT_WRAPPER = attrValue;
70 else if(attrName.equals("expr-wrapper"))
71 EXPR_WRAPPER = attrValue;
72 else if(attrName.equals("ws-gobbling"))
73 WS_GOBBLING = attrValue.equals("true");
74 else
75 throw new JstpParserException("page directive attribute "+attrName+" not supported", elem.row, elem.col);
76 }
77 break;
78
79 default :
80 throw new JstpParserException("internal error, unknown segment type "+elem.type, elem.row, elem.col);
81 }
82 }
83
84 out.println("/* The following code was generated by JstpTranslator on "+Calendar.getInstance().getTime()+" */");
85 if(pkgName!=null)
86 out.println("package "+pkgName+";");
87 out.println();
88
89 if(importList!=null)
90 {
91 StringTokenizer tknz = new StringTokenizer(importList, ", \t\f\r\n");
92 while(tknz.hasMoreTokens())
93 out.println("import "+tknz.nextToken()+";");
94 out.println();
95 }
96
97 out.print("public class "+className);
98 if(superClassName!=null)
99 out.println(" extends "+superClassName);
100 else
101 out.println();
102 out.println("{");
103
104 for(Iterator iter=declarations.iterator(); iter.hasNext();)
105 {
106 JstpElement decl = (JstpElement)iter.next();
107 out.println(decl.content.toString());
108 }
109
110 out.println(INDENTATION+RENDER_METHOD);
111 out.println(INDENTATION+"{");
112 for(Iterator iter=renderSegments.iterator(); iter.hasNext();)
113 {
114 JstpElement elem = (JstpElement)iter.next();
115 switch(elem.type)
116 {
117 case JstpElement.TYPE_SCRIPTLET :
118 out.println(elem.content.toString());
119 break;
120
121 case JstpElement.TYPE_EXPRESSION :
122 out.print(INDENTATION+INDENTATION +TEXT_WRAPPER+'(' );
123 out.print( EXPR_WRAPPER+'('+elem.content+')' );
124 out.println(");");
125 break;
126
127 case JstpElement.TYPE_TEXT :
128 boolean gobbled = false;
129 if(WS_GOBBLING && isBlank(elem.content))
130 {
131 char last = elem.content.charAt(elem.content.length()-1);
132 JstpElement porn = (last=='\r' || last=='\n')? elem.prev : elem.next;
133 gobbled = porn!=null && porn.type!=JstpElement.TYPE_TEXT && porn.type!=JstpElement.TYPE_EXPRESSION;
134 }
135 if(!gobbled)
136 {
137 out.print(INDENTATION+INDENTATION +TEXT_WRAPPER+'(' );
138 out.print( toJavaStringLiteral(elem.content) );
139 out.println(");");
140 }
141 break;
142
143 default :
144 throw new Error("assertion error, not a render segment type "+elem.type);
145 }
146 }
147 out.println(INDENTATION+"}");
148
149 out.println("}");
150 }
151
152 static public void main(String args[]) throws Exception
153 {
154 if(args.length!=1 && args.length!=2)
155 {
156 System.out.println("usage: java net.sf.jstp.JstpTranslator jstpFile [javaFile]");
157 System.exit(1);
158 }
159
160 File srcFile = new File(args[0]);
161 String className = srcFile.getName();
162 int y = className.indexOf('.');
163 if(y!=-1)
164 className = className.substring(0, y);
165
166 File desFile = args.length==1 ?
167 new File(srcFile.getParent(), className+".java") :
168 new File(args[1]);
169 if(!desFile.getParentFile().exists())
170 desFile.getParentFile().mkdirs();
171
172 InputStream in = null;
173 OutputStream out = null;
174 try
175 {
176 in = new BufferedInputStream(new FileInputStream(srcFile));
177 out = new BufferedOutputStream(new FileOutputStream(desFile));
178 Reader reader = new InputStreamReader(in);
179 Writer writer = new OutputStreamWriter(out);
180
181 try
182 {
183 JstpTranslator.translate(null, className, reader, writer);
184 System.out.println("generated "+desFile);
185 }
186 catch (JstpParserException e)
187 {
188 throw new Error("error translating "+srcFile+" ("+e.row+":"+e.col+") "+e, e);
189 }
190
191 writer.flush();
192 }
193 finally
194 {
195 if(out!=null) out.close();
196 if(in!=null) in.close();
197 }
198 }
199
200
201
202
203 static boolean isBlank(StringBuffer buf)
204 {
205 for(int i=0; i<buf.length(); i++)
206 {
207 if(!Character.isSpace(buf.charAt(i)))
208 return false;
209 }
210 return true;
211 }
212 static String toJavaStringLiteral(StringBuffer in)
213 {
214 StringBuffer out = new StringBuffer();
215 out.append('"');
216 for(int i=0; i<in.length(); i++)
217 {
218 char c = in.charAt(i);
219 switch(c)
220 {
221 case '\b' : out.append('//').append('b'); break;
222 case '\t' : out.append('//').append('t'); break;
223 case '\n' : out.append('//').append('n'); break;
224 case '\f' : out.append('//').append('f'); break;
225 case '\r' : out.append('//').append('r'); break;
226 case '\"' : out.append('//').append('\"'); break;
227 case '//' : out.append('//').append('//'); break;
228 default : out.append(c);
229 }
230 }
231 out.append('"');
232 return out.toString();
233 }
234
235
236
237 }