codemate.Fortran.FortranTemplater.java Source code

Java tutorial

Introduction

Here is the source code for codemate.Fortran.FortranTemplater.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Li Dong.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Li Dong - initial API and implementation
 ******************************************************************************/
package codemate.Fortran;

/**
 * FortranTemplater
 *
 * This class instantiates the template instances in the Fortran codes that have
 * been parsed.
 *
 * @author      Li Dong <dongli@lasg.iap.ac.cn>
 */

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;

import codemate.Fortran.FortranParser.*;
import codemate.project.*;
import codemate.templates.*;
import codemate.ui.*;
import codemate.utils.*;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;

public class FortranTemplater extends FortranBaseVisitor<Void> {
    private static List<TemplateBundle> templateBundles = new LinkedList<TemplateBundle>();
    private boolean encounterTemplateInstance;

    public static void loadBuiltinTemplates() {
        if (templateBundles.size() == 0) {
            templateBundles.add(new FortranListTemplate());
        }
    }

    public static void loadCompiledTemplates(File dir) {
        loadBuiltinTemplates();
        if (!dir.isDirectory())
            return;
        UI.notice("codemate", "Load compiled templates in " + dir.getPath() + ".");
        List<String> templateNames = new LinkedList<String>();
        for (String fileName : dir.list()) {
            if (fileName.endsWith("Template.class"))
                templateNames.add(fileName.split("\\.")[0]);
        }
        try {
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { dir.toURI().toURL() });
            for (String templateName : templateNames) {
                Class<?> cls = null;
                cls = classLoader.loadClass(templateName);
                templateBundles.add((TemplateBundle) cls.newInstance());
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * load
     * 
     * This method loads templates within the given project. The compiled
     * templates are in <project root>/.codemate/compiled_templates.
     * 
     * @param project
     */
    public static void load(Project project) {
        loadBuiltinTemplates();
        File compiledTemplatesRoot = new File(project.getRoot() + "/.codemate/compiled_templates");
        List<String> templateNames = new LinkedList<String>();
        for (File dir : project.getDirectories()) {
            for (File file : dir.listFiles()) {
                if (file.getName().endsWith("Template.java")) {
                    String templateName = file.getName().split("\\.")[0];
                    boolean needCompile = true;
                    if (compiledTemplatesRoot.isDirectory()) {
                        File classFile = new File(
                                compiledTemplatesRoot.getAbsolutePath() + "/" + templateName + ".class");
                        if (classFile.isFile() && file.lastModified() < classFile.lastModified()) {
                            UI.notice("codemate", "Use template in " + classFile.getPath() + ".");
                            needCompile = false;
                        }
                    } else {
                        UI.notice("codemate", "Create " + compiledTemplatesRoot.getAbsolutePath()
                                + " to store compiled templates.");
                        compiledTemplatesRoot.mkdirs();
                    }
                    if (needCompile) {
                        UI.notice("codemate", "Compile template in " + file.getPath() + ".");
                        SystemUtils.compile(file);
                        File classFile = new File(file.getParent() + "/" + templateName + ".class");
                        classFile.renameTo(
                                new File(compiledTemplatesRoot.getAbsolutePath() + "/" + classFile.getName()));
                    }
                    templateNames.add(templateName);
                }
            }
        }
        try {
            URLClassLoader classLoader = URLClassLoader
                    .newInstance(new URL[] { compiledTemplatesRoot.toURI().toURL() });
            for (String templateName : templateNames) {
                Class<?> cls = null;
                cls = classLoader.loadClass(templateName);
                templateBundles.add((TemplateBundle) cls.newInstance());
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * searchTemplateBundle
     * 
     * This method searches the template bundle with the given name.
     * 
     * @param bundleName
     */
    private static TemplateBundle searchTemplateBundle(String bundleName) {
        for (TemplateBundle templateBundle : templateBundles)
            if (templateBundle.containsTemplate(bundleName))
                return templateBundle;
        return null;
    }

    /**
     * instantiate
     *
     * This method visits the tree to find out template instances and
     * instantiate them if there is a template matched. Note the visiting will
     * be looped until there is no more template instance.
     *
     * @param       tree        The parse tree
     * @return      boolean     Return true if encounter template instance.
     *
     * @author      Li Dong <dongli@lasg.iap.ac.cn>
     */
    public boolean instantiate(ParseTree tree) {
        boolean res = false;
        do {
            encounterTemplateInstance = false;
            visit(tree);
            if (encounterTemplateInstance)
                res = true;
        } while (encounterTemplateInstance);
        return res;
    }

    public Void visitTemplateInstance(FortranParser.TemplateInstanceContext ctx) {
        TemplateBundle matchedTemplateBundle = searchTemplateBundle(ctx.id().getText());
        if (matchedTemplateBundle != null) {
            encounterTemplateInstance = true;
            if (ctx.getParent().getRuleIndex() == FortranParser.RULE_extendsAttribute) {
                // Note: Because the extended type is special, so we handle
                //       them individually.
                handleTemplateInExtendsAttribute(ctx, matchedTemplateBundle);
            } else {
                handleNormalTemplate(ctx, matchedTemplateBundle);
            }
        }
        return null;
    }

    @SuppressWarnings("unused")
    private Stack<ParserRuleContext> findRoute(ParserRuleContext startNode, int endNodeRuleIndex) {
        Stack<ParserRuleContext> route = new Stack<ParserRuleContext>();
        ParserRuleContext node = startNode;
        boolean isFound = false;
        while (!isFound && node != null) {
            route.push(node);
            if (node.getRuleIndex() == endNodeRuleIndex)
                isFound = true;
            else
                for (ParseTree child : node.children)
                    if (child instanceof ParserRuleContext) {
                        if (((ParserRuleContext) child).getRuleIndex() == endNodeRuleIndex) {
                            route.pop(); // throw the useless parent node away
                            route.push((ParserRuleContext) child);
                            // I push a null into the stack to indicate that
                            // the end node is a sibling node.
                            route.push(null);
                            isFound = true;
                            break;
                        }
                    }
            if (!isFound)
                node = node.getParent();
        }
        if (false) {
            if (route.lastElement() != null)
                System.out.println("Route (local):");
            else
                System.out.println("Route (non-local):");
            for (ParserRuleContext node_ : route)
                if (node_ != null)
                    System.out.println("* " + FortranParser.ruleNames[node_.getRuleIndex()]);
        }
        return route;
    }

    private void applyTemplateInstance(Template template, String instance, ParserRuleContext templateNode) {
        // parse the new node
        ParserRuleContext newNode = null;
        try {
            newNode = (ParserRuleContext) FortranProcessor.callParser(template.getType(), instance);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // place the new node
        Stack<ParserRuleContext> route = findRoute(templateNode, template.getType());
        ParserRuleContext rootNode = null, markNode = null;
        // ---------------------------------------------------------------------
        // local modification
        if (route.lastElement() != null) {
            rootNode = route.pop();
            markNode = route.pop();
            for (ParseTree child : rootNode.children) {
                if (child == markNode) {
                    switch (template.getAction()) {
                    case REPLACE:
                        rootNode.children.addAll(rootNode.children.indexOf(child), newNode.children);
                        rootNode.children.remove(child);
                        break;
                    case APPEND:
                        rootNode.children.addAll(rootNode.children.indexOf(child) + 1, newNode.children);
                        break;
                    case PREPEND:
                        rootNode.children.addAll(rootNode.children.indexOf(child), newNode.children);
                        break;
                    }
                    break;
                }
            }
        }
        // ---------------------------------------------------------------------
        // non-local modification
        else {
            route.pop(); // throw the indicate element away
            rootNode = route.pop();
            if (rootNode.children == null)
                rootNode.children = new LinkedList<ParseTree>();
            switch (template.getAction()) {
            case REPLACE:
                UI.error("FortranTemplater",
                        "Sorry, CodeMate does not support non-local replacing" + " when instantiating template!");
                break;
            case APPEND:
                // Note: Since adding C preprocessor grammar, we need to check
                //       if the last node is a stub of C preprocessor directive.
                //       If so, we need insert new node before it.
                int loc = rootNode.children.size() - 1;
                if (loc != -1 && rootNode.children.get(loc) instanceof CppDirectiveContext)
                    rootNode.children.addAll(loc, newNode.children);
                else
                    rootNode.children.addAll(newNode.children);
                break;
            case PREPEND:
                rootNode.children.addAll(0, newNode.children);
                break;
            }
        }
    }

    private void handleNormalTemplate(TemplateInstanceContext templateNode, TemplateBundle templateBundle) {
        // prepare template arguments
        List<String> templateArguments = null;
        if (templateNode.templateArguments() != null) {
            templateArguments = new ArrayList<String>();
            for (TemplateArgumentContext arg : templateNode.templateArguments().templateArgument()) {
                FortranRewriter rewriter = new FortranRewriter();
                rewriter.visitTemplateArgument(arg);
                templateArguments.add(rewriter.getNewCode());
            }
        }
        // prepare template block
        String templateBlock = null;
        if (templateNode.templateBlock() != null) {
            templateBlock = new String();
            FortranRewriter rewriter = new FortranRewriter();
            rewriter.visitTemplateBlock(templateNode.templateBlock());
            templateBlock = rewriter.getNewCode();
        }
        // instantiate template
        Map<Template, String> templateInstances = templateBundle.instantiate(templateNode.id().getText(),
                templateArguments, templateBlock);
        // apply template instances to the parse tree
        for (Entry<Template, String> instance : templateInstances.entrySet()) {
            applyTemplateInstance(instance.getKey(), instance.getValue(), templateNode);
        }
    }

    private void handleTemplateInExtendsAttribute(TemplateInstanceContext templateNode,
            TemplateBundle templateBundle) {
        TypeDeclarationStatementContext typeDecl = (TypeDeclarationStatementContext) templateNode.getParent()
                .getParent().getParent().getParent();
        // prepare template arguments
        List<String> templateArguments = new ArrayList<String>();
        for (TemplateArgumentContext arg : templateNode.templateArguments().templateArgument()) {
            FortranRewriter rewriter = new FortranRewriter();
            rewriter.visitTemplateArgument(arg);
            templateArguments.add(rewriter.getNewCode());
        }
        if (typeDecl.dataDeclarationStatements() != null) {
            // add data declaration statements into arguments for
            // inserting them in the extended type declaration
            // statement
            FortranRewriter rewriter = new FortranRewriter();
            rewriter.visit(typeDecl.dataDeclarationStatements());
            templateArguments.add(rewriter.getNewCode());
        } else {
            templateArguments.add("");
        }
        if (typeDecl.containedTypeBoundProcedures() != null) {
            // add type bound procedures into arguments for
            // inserting them in the extended type declaration
            // statement
            FortranRewriter rewriter = new FortranRewriter();
            rewriter.visit(typeDecl.containedTypeBoundProcedures().typeBoundProcedureStatements());
            templateArguments.add(rewriter.getNewCode());
        } else {
            templateArguments.add("");
        }
        // instantiate template
        Map<Template, String> templateInstances = templateBundle.instantiate(templateNode.id().getText(),
                templateArguments, null);
        // apply template instances to the parse tree
        for (Entry<Template, String> instance : templateInstances.entrySet()) {
            applyTemplateInstance(instance.getKey(), instance.getValue(), templateNode);
        }
    }

    /**
     * visitChildren
     * 
     * This method overrides the original one which will be interfered when I
     * make changes to the parse tree (e.g. inserting multiple nodes) by
     * changing the loop style.
     */
    public Void visitChildren(RuleNode node) {
        Void result = defaultResult();
        for (int i = 0; i < node.getChildCount(); i++) {
            if (!shouldVisitNextChild(node, result))
                break;
            ParseTree c = node.getChild(i);
            Void childResult = c.accept(this);
            result = aggregateResult(result, childResult);
        }
        return result;
    }
}