1/*
2 [The "BSD licence"]
3 Copyright (c) 2009 Shaoting Cai
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 1. Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in the
13    documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15    derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28package org.antlr.gunit.swingui.model;
29
30import org.antlr.gunit.swingui.parsers.ANTLRv3Lexer;
31import org.antlr.gunit.swingui.parsers.ANTLRv3Parser;
32import org.antlr.gunit.swingui.parsers.StGUnitLexer;
33import org.antlr.gunit.swingui.parsers.StGUnitParser;
34import org.antlr.gunit.swingui.runner.TestSuiteAdapter;
35import org.antlr.runtime.ANTLRReaderStream;
36import org.antlr.runtime.CommonTokenStream;
37import org.antlr.stringtemplate.StringTemplate;
38import org.antlr.stringtemplate.StringTemplateGroup;
39
40import java.io.*;
41import java.util.ArrayList;
42import java.util.List;
43
44public class TestSuiteFactory {
45
46    private static String TEMPLATE_FILE = "org/antlr/gunit/swingui/gunit.stg";
47    private static StringTemplateGroup templates;
48    public static final String TEST_SUITE_EXT = ".gunit";
49    public static final String GRAMMAR_EXT = ".g";
50
51    static  {
52        ClassLoader loader = TestSuiteFactory.class.getClassLoader();
53        InputStream in = loader.getResourceAsStream(TEMPLATE_FILE);
54        if ( in == null ) {
55            throw new RuntimeException("internal error: Can't find templates "+TEMPLATE_FILE);
56        }
57        Reader rd = new InputStreamReader(in);
58        templates = new StringTemplateGroup(rd);
59    }
60
61    /**
62     * Factory method: create a testsuite from ANTLR grammar.  Save the test
63     * suite file in the same directory of the grammar file.
64     * @param grammarFile ANTLRv3 grammar file.
65     * @return test suite object
66     */
67    public static TestSuite createTestSuite(File grammarFile) {
68        if(grammarFile != null && grammarFile.exists() && grammarFile.isFile()) {
69
70            final String fileName = grammarFile.getName();
71            final String grammarName = fileName.substring(0, fileName.lastIndexOf('.'));
72            final String grammarDir = grammarFile.getParent();
73            final File testFile = new File(grammarDir + File.separator + grammarName + TEST_SUITE_EXT);
74
75            final TestSuite result = new TestSuite(grammarName, testFile);
76            result.rules = loadRulesFromGrammar(grammarFile);
77
78            if(saveTestSuite(result)) {
79                return result;
80            } else {
81                throw new RuntimeException("Can't save test suite file.");
82            }
83        } else {
84            throw new RuntimeException("Invalid grammar file.");
85        }
86    }
87
88
89    /* Load rules from an ANTLR grammar file. */
90    private static List<Rule> loadRulesFromGrammar(File grammarFile) {
91
92        // get all the rule names
93        final List<String> ruleNames = new ArrayList<String>();
94        try {
95            final Reader reader = new BufferedReader(new FileReader(grammarFile));
96            final ANTLRv3Lexer lexer = new ANTLRv3Lexer(new ANTLRReaderStream(reader));
97            final CommonTokenStream tokens = new CommonTokenStream(lexer);
98            final ANTLRv3Parser parser = new ANTLRv3Parser(tokens);
99            parser.rules = ruleNames;
100            parser.grammarDef();
101            reader.close();
102        } catch (Exception e) {
103            e.printStackTrace();
104        }
105
106        // convert to rule object
107        final List<Rule> ruleList = new ArrayList<Rule>();
108        for(String str: ruleNames) {
109            ruleList.add(new Rule(str));
110        }
111
112        return ruleList;
113    }
114
115    /* Save testsuite to *.gunit file. */
116    public static boolean saveTestSuite(TestSuite testSuite) {
117        final String data = getScript(testSuite);
118        try {
119            FileWriter fw = new FileWriter(testSuite.getTestSuiteFile());
120            fw.write(data);
121            fw.flush();
122            fw.close();
123        } catch (IOException e) {
124            e.printStackTrace();
125            return false;
126        }
127        return true;
128    }
129
130    /**
131     * Get the text script from the testSuite.
132     * @param testSuite
133     * @return test script
134     */
135    public static String getScript(TestSuite testSuite) {
136        if(testSuite == null) return null;
137        StringTemplate gUnitScript = templates.getInstanceOf("gUnitFile");
138        gUnitScript.setAttribute("testSuite", testSuite);
139
140        return gUnitScript.toString();
141    }
142
143    /**
144     * From textual script to program model.
145     * @param file testsuite file (.gunit)
146     * @return test suite object
147     */
148    public static TestSuite loadTestSuite(File file) {
149        if ( file.getName().endsWith(GRAMMAR_EXT) ) {
150            throw new RuntimeException(file.getName()+" is a grammar file not a gunit file");
151        }
152        // check grammar file
153        final File grammarFile = getGrammarFile(file);
154        if(grammarFile == null)
155            throw new RuntimeException("Can't find grammar file associated with gunit file: "+file.getAbsoluteFile());
156
157        TestSuite result = new TestSuite("", file);
158
159        // read in test suite
160        try {
161            final Reader reader = new BufferedReader(new FileReader(file));
162            final StGUnitLexer lexer = new StGUnitLexer(new ANTLRReaderStream(reader));
163            final CommonTokenStream tokens = new CommonTokenStream(lexer);
164            final StGUnitParser parser = new StGUnitParser(tokens);
165            final TestSuiteAdapter adapter = new TestSuiteAdapter(result);
166            parser.adapter = adapter;
167            parser.gUnitDef();
168            result.setTokens(tokens);
169            reader.close();
170        } catch (Exception ex) {
171            throw new RuntimeException("Error reading test suite file.\n" + ex.getMessage());
172        }
173
174        // load un-tested rules from grammar
175        final List<Rule> completeRuleList = loadRulesFromGrammar(grammarFile);
176        for(Rule rule: completeRuleList) {
177            if(!result.hasRule(rule)) {
178                result.addRule(rule);
179                //System.out.println("Add rule:" + rule);
180            }
181        }
182
183        return result;
184    }
185
186    /**
187     * Get the grammar file of the testsuite file in the same directory.
188     * @param testsuiteFile
189     * @return grammar file or null
190     */
191    private static File getGrammarFile(File testsuiteFile) {
192        String sTestFile;
193        try {
194            sTestFile = testsuiteFile.getCanonicalPath();
195        }
196        catch (IOException e) {
197            return null;
198        }
199        // Try Foo.g from Foo.gunit
200        String fname =
201            sTestFile.substring(0, sTestFile.lastIndexOf('.')) + GRAMMAR_EXT;
202        File fileGrammar = new File(fname);
203        if(fileGrammar.exists() && fileGrammar.isFile()) return fileGrammar;
204        // Try FooParser.g from Foo.gunit
205        fname = sTestFile.substring(0, sTestFile.lastIndexOf('.'))+"Parser"+GRAMMAR_EXT;
206        if(fileGrammar.exists() && fileGrammar.isFile()) return fileGrammar;
207        return fileGrammar;
208    }
209}
210