EMMA Coverage Report (generated Mon Mar 20 21:34:30 PST 2006)
[all classes][com.moesol.generator.core]

COVERAGE SUMMARY FOR SOURCE FILE [Template.java]

nameclass, %method, %block, %line, %
Template.java100% (3/3)71%  (17/24)53%  (351/664)62%  (73.4/119)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Template$2100% (1/1)75%  (3/4)28%  (64/229)41%  (12.7/31)
callWithArg (String, String): void 0%   (0/1)0%   (0/102)0%   (0/12)
callSimple (String): void 100% (1/1)46%  (40/87)58%  (7/12)
nameParsed (String): String 100% (1/1)48%  (15/31)79%  (4.8/6)
Template$2 (Template, Writer): void 100% (1/1)100% (9/9)100% (1/1)
     
class Template$1100% (1/1)100% (2/2)44%  (20/45)80%  (4/5)
nameParsed (String): String 100% (1/1)31%  (11/36)75%  (3/4)
Template$1 (Template, Properties): void 100% (1/1)100% (9/9)100% (1/1)
     
class Template100% (1/1)67%  (12/18)68%  (267/390)68%  (56.6/83)
Template (InputStream): void 0%   (0/1)0%   (0/15)0%   (0/4)
getErrorPrefix (): String 0%   (0/1)0%   (0/3)0%   (0/1)
getInput (): Reader 0%   (0/1)0%   (0/3)0%   (0/1)
quoteSeen (Writer): void 0%   (0/1)0%   (0/28)0%   (0/8)
setErrorPrefix (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
throwUnwrappedException (InvocationTargetException): void 0%   (0/1)0%   (0/12)0%   (0/4)
needOpenBrace (Properties, NameParsedListener, char): String 100% (1/1)42%  (15/36)60%  (3/5)
apply (Properties, Writer): void 100% (1/1)71%  (46/65)86%  (12/14)
scanName (Properties, NameParsedListener): String 100% (1/1)87%  (104/120)94%  (15/16)
writeChar (Writer, int): void 100% (1/1)88%  (7/8)75%  (3/4)
escapeSeen (Writer): void 100% (1/1)92%  (12/13)75%  (3/4)
Template (Reader): void 100% (1/1)100% (12/12)100% (4/4)
Template (String): void 100% (1/1)100% (15/15)100% (4/4)
callName (Properties, Writer): void 100% (1/1)100% (13/13)100% (3/3)
getObject (): Object 100% (1/1)100% (3/3)100% (1/1)
replaceName (Properties, Writer, char): void 100% (1/1)100% (29/29)100% (6/6)
setInput (Reader): void 100% (1/1)100% (7/7)100% (2/2)
setObject (Object): void 100% (1/1)100% (4/4)100% (2/2)

1/*
2 * $Id: Template.java,v 1.4 2004/11/04 19:27:37 hastings Exp $
3 * 
4 * Copyright (c) 2004, Moebius Solutions, Inc.
5 * All rights reserved.
6 * 
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 
11 *     Redistributions of source code must retain the above copyright
12 *     notice, this list of conditions and the following disclaimer.
13 * 
14 *     Redistributions in binary form must reproduce the above
15 *     copyright notice, this list of conditions and the following
16 *     disclaimer in the documentation and/or other materials provided
17 *     with the distribution.
18 * 
19 *     Neither the name of Moebius Solutions, Inc. nor the names of
20 *     its contributors may be used to endorse or promote products
21 *     derived from this software without specific prior written
22 *     permission.
23 * 
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37 
38package com.moesol.generator.core;
39 
40import java.io.*;
41import java.util.*;
42import java.lang.reflect.*;
43 
44 
45interface NameParsedListener {
46    public String nameParsed(String name) throws ApplyException;
47}
48 
49/**
50 * Process a template file and replace occurances of ${foo} with the
51 * value of the property foo.
52 */
53public class Template {
54    public Template(InputStream is) {
55        in = new LineNumberReader(new InputStreamReader(is));
56    }
57    public Template(Reader r) {
58        in = new LineNumberReader(r);
59    }
60    public Template(String t) {
61            in = new LineNumberReader(new StringReader(t));
62    }
63    public void setInput(Reader r) {
64        in = new LineNumberReader(r);
65    }
66    /**
67     * @return the input reader used for the source of the template.
68     * Note that input reader set by the constructor and setInput is
69     * wrapped by a LineNumberReader, which this method returns.
70     */
71    public Reader getInput() {
72        return in;
73    }
74    public void setObject(Object o) {
75        object = o;
76    }
77    public Object getObject() {
78        return object;
79    }
80    /**
81     * Message to prepend to thrown error messages.
82     */
83    public void setErrorPrefix(String info) {
84        error_info = info;
85    }
86    public String getErrorPrefix() {
87        return error_info;
88    }
89    public void apply(Properties p, Writer out)
90        throws ApplyException
91    {
92        try {
93            int c;
94            while ((c = in.read()) != -1) {
95                if (c == '$') {
96                    replaceName(p, out, '$');
97                } else if (c == '#') {
98                    callName(p, out);
99                } else if (c == '\\') {
100                    escapeSeen(out);
101                } else if (c == '"') {
102                    writeChar(out, c);
103                    // Do not treat quotes specially.
104                    // Replace ${xx} inside of quotes.
105                    // quoteSeen(out);
106                } else {
107                    writeChar(out, c);
108                }
109            }
110        } catch (IOException e) {
111            throw new ApplyException(error_info + "io error at: " + in.getLineNumber(), e);
112        }
113    }
114    
115    private void replaceName(final Properties p, Writer out, char cause)
116        throws ApplyException, IOException
117    {
118        NameParsedListener l = new NameParsedListener() {
119                public String nameParsed(String name) throws ApplyException {
120                    String replace = p.getProperty(name.toString());
121                    if (null == replace) {
122                        throw new ApplyException(error_info + "line: "
123                                                 + in.getLineNumber()
124                                                 + ": No property for ${"+name+"}");
125                    }
126                    return replace;
127                }
128            };
129        String replacement = needOpenBrace(p, l, cause);
130 
131        Template t = new Template(new StringReader(replacement));
132        t.setObject(t.getObject());
133        t.apply(p, out);
134    }
135    private void callName(final Properties p, final Writer out)
136        throws ApplyException, IOException
137    {
138        NameParsedListener l = new NameParsedListener() {
139                public String nameParsed(String name) throws ApplyException {
140                    // TODO better arg parsing
141                    int left = name.indexOf('(');
142                    int right = name.lastIndexOf(')');
143                    if (left > 0 && right > left) {
144                        callWithArg(name.substring(0,left), name.substring(left+1, right));
145                    } else {
146                        callSimple(name);
147                    }
148                    return "";
149                }
150                private void callSimple(String name) throws ApplyException {
151                    Class c = object.getClass();
152                    Class param_types[] = { Writer.class };
153                    Object params[] = { out };
154                    try {
155                        Method m = c.getMethod(name, param_types);
156                        m.invoke(object, params);
157                    } catch (InvocationTargetException e) {
158                        throwUnwrappedException(e);
159                        throw new ApplyException(error_info + "error: " + in.getLineNumber(), e);
160                    } catch (Exception e) {
161                        throw new ApplyException(error_info + "error: " + in.getLineNumber(), e);
162                    }
163                }
164                private void callWithArg(String name, String arg) throws ApplyException {
165                    Class c = object.getClass();
166                    Class param_types[] = { Writer.class, String.class };
167                    Object params[] = { out, arg };
168                    try {
169                        Method m = c.getMethod(name, param_types);
170                        m.invoke(object, params);
171                    } catch (InvocationTargetException e) {
172                        throwUnwrappedException(e);
173                        throw new ApplyException(error_info + "error: " + in.getLineNumber(), e);
174                    } catch (Exception e) {
175                        throw new ApplyException(error_info + "error: " + in.getLineNumber(), e);
176                    }
177                }
178            };
179        needOpenBrace(p, l, '#');
180    }
181    private void throwUnwrappedException(InvocationTargetException e)
182        throws ApplyException
183    {
184        if (e.getCause() != null) {
185            if (e.getCause() instanceof ApplyException) {
186                throw (ApplyException)e.getCause();
187            }
188        }
189    }
190    private void escapeSeen(Writer out) throws IOException {
191        int c;
192        if ((c = in.read()) == -1) {
193            return;
194        }
195        writeChar(out, c);
196    }
197    private void quoteSeen(Writer out) throws IOException {
198        int c;
199        while ((c = in.read()) != -1) {
200            if (c == '\\') {
201                escapeSeen(out);
202            } else if (c == '"') {
203                writeChar(out, c);
204                return;
205            } else {
206                writeChar(out, c);
207            }
208        }
209    }
210 
211    private String needOpenBrace(Properties p, NameParsedListener l, char cause)
212        throws ApplyException, IOException
213    {
214        int c;
215        if ((c = in.read()) == -1) {
216            // Caused by "foo$" or "bar#" input escape the cause and
217            // return
218            return "\\" + cause;
219        }
220        if (c == '{') {
221            return scanName(p, l);
222        }
223        // Caused by "foo$bar" input, just give up on the
224        // replacement and return the escaped cause plus bad char.
225        return "\\" + cause + (char)c;
226    }
227    private String scanName(Properties p, NameParsedListener l)
228        throws IOException, ApplyException
229    {
230        StringBuffer name = new StringBuffer();
231    
232        int c;
233        while ((c = in.read()) != -1) {
234            if (c == '}') {
235                return l.nameParsed(name.toString());
236            } else if (c == '$') {
237                name.append(needOpenBrace(p, l, '$'));
238            } else if (c == '\\') {
239                StringWriter sw = new StringWriter(1);
240                escapeSeen(sw);
241                name.append(sw.getBuffer());
242            } else if (c == '#') {
243                throw new ApplyException(error_info + "parse error at: "
244                                         + in.getLineNumber()
245                                         + ", nested calls not allowed in ${"
246                                         + name.toString());
247            } else if (c == '\n') {
248                throw new ApplyException(error_info + "parse error at: "
249                                         + in.getLineNumber()
250                                         + ", newline embedded in ${"
251                                         + name.toString());
252            } else {
253                name.append((char)c);
254            }
255        }
256        throw new ApplyException("at: " + in.getLineNumber() + " EOF looking for } ");
257    }
258 
259    /**
260     * Write c to out, but skipping CR characters.
261     */
262    private void writeChar(Writer out, int c) throws IOException {
263        if (c == '\r') {
264            return;
265        }
266        out.write(c);
267    }
268 
269    private String error_info = "";
270    private LineNumberReader in;
271    private Object object;
272}

[all classes][com.moesol.generator.core]
EMMA 2.0.5312 (C) Vladimir Roubtsov