001    /*
002     * mesopotamia @mesopotamia.version@
003     * Multilingual parser and repository. 
004     * Copyright (C) 2005  Hammurapi Group
005     *
006     * This program is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 2 of the License, or (at your option) any later version.
010     *
011     * This program is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with this library; if not, write to the Free Software
018     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
019     *
020     * URL: http://http://www.hammurapi.biz
021     * e-Mail: support@hammurapi.biz
022     */
023    package org.mesopotamia.util;
024    
025    import java.io.File;
026    import java.io.FileInputStream;
027    import java.io.FileOutputStream;
028    import java.io.FileReader;
029    import java.io.FileWriter;
030    import java.io.IOException;
031    import java.io.InputStream;
032    import java.io.PrintStream;
033    import java.io.Reader;
034    import java.util.ArrayList;
035    import java.util.Collection;
036    import java.util.HashMap;
037    import java.util.HashSet;
038    import java.util.Iterator;
039    import java.util.Map;
040    import java.util.Set;
041    import java.util.logging.Level;
042    import java.util.logging.Logger;
043    
044    import org.mesopotamia.MesopotamiaException;
045    import org.mesopotamia.MesopotamiaRuntimeException;
046    import org.onemind.jxp.CachedJxpPage;
047    import org.onemind.jxp.CachingPageSource;
048    import org.onemind.jxp.JxpContext;
049    import org.onemind.jxp.JxpPageSource;
050    import org.onemind.jxp.JxpProcessor;
051    
052    import antlr.ASTFactory;
053    import antlr.RecognitionException;
054    import antlr.TokenStreamException;
055    import antlr.collections.AST;
056    import biz.hammurapi.util.CollectionVisitable;
057    import biz.hammurapi.util.VisitableBase;
058    import biz.hammurapi.util.Visitor;
059    
060    public class BnfModel extends VisitableBase {
061            
062            private static final Logger logger = Logger.getLogger(BnfModel.class.getName());
063            
064            private Collection<Object> ruleDefinitions=new ArrayList<Object>();
065            /**
066             * Keeps mapping subclass (String) -> superclass (RuleDefinition)
067             */
068            Map<String, RuleDefinition> superClasses=new HashMap<String, RuleDefinition>();
069            Map<String, Collection<RuleDefinition>> interfaces=new HashMap<String, Collection<RuleDefinition>>();
070            Map<MatchPath, RuleDefinition> matchPath=new HashMap<MatchPath, RuleDefinition>();
071            Map<String, RuleDefinition> rulesMap=new HashMap<String, RuleDefinition>(); // name -> rule
072            private String packageName;
073            private Class tokenTypesClass;
074            
075            void addInterface(String implementor, RuleDefinition theInterface) {
076                    Collection<RuleDefinition> interfaceCollection=interfaces.get(implementor);
077                    if (interfaceCollection==null) {
078                            interfaceCollection=new ArrayList<RuleDefinition>();
079                            interfaces.put(implementor, interfaceCollection);
080                    }
081                    interfaceCollection.add(theInterface);
082                    
083                    // Validation that no interface extends class.
084                    Iterator<Object> it=ruleDefinitions.iterator();
085                    while (it.hasNext()) {
086                            RuleDefinition rule=(RuleDefinition) it.next();
087                            if (rule.isInterface() && !(rule.getSuperRule()==null || rule.getSuperRule().isInterface())) {
088                                    System.err.println("Interface rule "+rule.getName()+" extends class rule "+rule.getSuperClassName());
089                            }
090                    }               
091            }
092            
093            public BnfModel(Reader r, Class tokenTypesClass, String rootLanguageElementClassName) throws MesopotamiaException {
094                    this.tokenTypesClass=tokenTypesClass;
095                    try {
096                            BnfLexer lexer=new BnfLexer(r);
097                            lexer.setTokenObjectClass(biz.hammurapi.antlr.Token.class.getName());
098                            BnfRecognizer parser=new BnfRecognizer(lexer);
099                            ASTFactory astFactory = new ASTFactory();
100                            astFactory.setASTNodeClass(biz.hammurapi.antlr.AST.class);
101                            parser.setASTFactory(astFactory);
102                            parser.grammar();
103                            AST ast=parser.getAST();
104                            
105                            PrintStream printStream = new PrintStream(new FileOutputStream("ast.txt"));
106                            ((biz.hammurapi.antlr.AST) ast).print(parser.getTokenNames(), printStream, 0, true);
107                            printStream.close();
108                            
109                            Set<String> names=new HashSet<String>();
110                            for (AST node=ast; node!=null; node=node.getNextSibling()) {
111                                    if (node.getType()==BnfTokenTypes.LITERAL_package) {
112                                            StringBuffer sb=new StringBuffer();
113                                            for (AST pnode=node.getFirstChild(); pnode!=null; pnode=pnode.getNextSibling()) {
114                                                    sb.append(pnode.getText());
115                                            }
116                                            packageName=sb.toString();
117                                    } else if (names.add(node.getText())) {
118                                            RuleDefinition ruleDefinition = new RuleDefinition(this, node, rootLanguageElementClassName);
119                                            ruleDefinitions.add(ruleDefinition);
120                                            rulesMap.put(ruleDefinition.getName(), ruleDefinition);
121                                    } else {
122                                            System.err.println("Rule already defined: "+node.getText());
123                                    }
124                            }                       
125                    } catch (TokenStreamException e) {
126                            throw new MesopotamiaException(e);
127                    } catch (IOException e) {
128                            throw new MesopotamiaException(e);
129                    } catch (RecognitionException e) {
130                            throw new MesopotamiaException(e);
131                    }
132                    
133            }
134            
135            protected void acceptChildren(Visitor visitor) {
136                    new CollectionVisitable(ruleDefinitions, false).accept(visitor);
137            }
138            
139            public void checkTokenName(String tokenName) throws MesopotamiaRuntimeException {
140                    if (tokenTypesClass!=null && !"*".equals(tokenName)) {
141                            try {
142                                    tokenTypesClass.getField(tokenName);
143                            } catch (SecurityException e) {
144                                    throw new MesopotamiaRuntimeException("Field "+tokenName+" cannot be accessed", e);
145                            } catch (NoSuchFieldException e) {
146                                    throw new MesopotamiaRuntimeException("Invalid token name: "+tokenName);
147                            }
148                    }
149            }
150            
151    
152            /**
153             * @param args
154             */
155            public static void main(final String[] args) throws Exception {
156                    System.out.println("Arguments: <bnf file> <token types class> <root language element class> <template directory> <output directory>");
157                    if (args.length!=5) {
158                            System.err.println("Invalid number of arguments");
159                            return;
160                    }
161                    
162                    BnfModel model = new BnfModel(new FileReader(args[0]), Class.forName(args[1]), args[2]);
163                    
164                    final File templateDir = new File(args[3]);
165                    
166                    final JxpPageSource pageSource = new CachingPageSource() {
167                            
168                            protected boolean isExpired(CachedJxpPage page) {
169                                    return false;
170                            }
171    
172                            protected boolean hasStream(String pageName) {
173                                    return new File(templateDir, pageName+".jxp").isFile();
174                            }
175    
176                            protected InputStream loadStream(CachedJxpPage page) throws IOException {
177                                    File pageFile = new File(templateDir, page.getName()+".jxp");
178                                    page.setPageTimepstamp(pageFile.lastModified());
179                                    return new FileInputStream(pageFile);
180                            }
181                    };
182                    
183                    final File outputDir = new File(args[4]);
184                    
185                    model.accept(new Visitor() {
186    
187                            public boolean visit(Object target) {
188                                    logger.info("Processing: "+target);
189                                    Map<String, Object> context = new HashMap<String, Object>();
190                                    JxpContext jxpContext = new JxpContext(pageSource, context);
191                                    JxpProcessor processor = new JxpProcessor(jxpContext);
192                                    try {
193                                            if (target instanceof RuleDefinition) {
194                                                    context.put("rule", target);
195                                                    RuleDefinition rd = (RuleDefinition) target;
196                                                    String templateName;
197                                                    if (rd.isFactory()) {
198                                                            templateName = "factory";
199                                                    } else if (rd.isInterface()) {
200                                                            templateName = "interface";
201                                                    } else {
202                                                            templateName = "class";
203                                                    }
204                                                    FileWriter writer = new FileWriter(new File(outputDir, rd.getName()+".java"));
205                                                    try {
206                                                            processor.process(templateName, writer);
207                                                    } finally {
208                                                            writer.close();
209                                                    }
210                                                    return false;
211                                            } else if (target instanceof BnfModel) {
212                                                    context.put("model", target);
213                                                    FileWriter writer = new FileWriter(new File(outputDir, "LanguageModel.java"));
214                                                    try {
215                                                            processor.process("model", writer);
216                                                    } finally {
217                                                            writer.close();
218                                                    }       
219                                            }
220                                    } catch (Exception e) {
221                                            logger.log(Level.WARNING, "Processing of "+target+" failed: "+e, e);
222                                    }
223                                    return true;
224                            }
225                            
226                    });                             
227            }
228    
229            public String getPackage() {
230                    return packageName;
231            }
232    }