All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dyuproject.protostuff.compiler.PluginProtoCompiler Maven / Gradle / Ivy

//========================================================================
//Copyright 2007-2010 David Yu [email protected]
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at 
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================

package com.dyuproject.protostuff.compiler;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.URL;

import org.antlr.stringtemplate.AutoIndentWriter;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;

import com.dyuproject.protostuff.parser.DefaultProtoLoader;
import com.dyuproject.protostuff.parser.EnumGroup;
import com.dyuproject.protostuff.parser.Message;
import com.dyuproject.protostuff.parser.Proto;
import com.dyuproject.protostuff.parser.ProtoUtil;

/**
 * A plugin proto compiler whose output relies on the 'output' param configured 
 * in {@link ProtoModule}. The output param should point to a StringTemplate resource 
 * (file, url, or from classpath). 
 *
 * @author David Yu
 * @created May 25, 2010
 */
public class PluginProtoCompiler extends STCodeGenerator
{
    
    /**
     * To enable, specify -Dppc.check_filename_placeholder=true
     */
    protected static final boolean CHECK_FILENAME_PLACEHOLDER = 
            Boolean.getBoolean("ppc.check_filename_placeholder");
    
    /**
     * Resolve the stg from the module.
     */
    public interface GroupResolver
    {
        
        /**
         * Resolve the stg from the module.
         */
        StringTemplateGroup resolveSTG(ProtoModule module);
    }
    
    public static final GroupResolver GROUP_RESOLVER = new GroupResolver()
    {
        
        public StringTemplateGroup resolveSTG(ProtoModule module)
        {
            String resource = module.getOutput();
            try
            {
                File file = new File(resource);
                if(file.exists())
                    return new StringTemplateGroup(new BufferedReader(new FileReader(file)));
                
                URL url = DefaultProtoLoader.getResource(resource, 
                        PluginProtoCompiler.class);
                if(url != null)
                {
                    return new StringTemplateGroup(new BufferedReader(
                            new InputStreamReader(url.openStream(), "UTF-8")));
                }
                if(resource.startsWith("http://"))
                {
                    return new StringTemplateGroup(new BufferedReader(
                            new InputStreamReader(new URL(resource).openStream(), "UTF-8")));
                }
            }
            catch(IOException e)
            {
                throw new RuntimeException(e);
            }
            throw new IllegalStateException("Could not find " + resource);
        }
    };
    
    public static void setGroupResolver(GroupResolver resolver)
    {
        if(resolver != null)
            __resolver = resolver;
    }
    
    /**
     * Returns null if template is not found.
     */
    public static StringTemplate getTemplateFrom(StringTemplateGroup group, 
            String template)
    {
        try
        {
            return group.lookupTemplate(template);
        }
        catch(IllegalArgumentException e)
        {
            return null;
        }
    }
    
    private static GroupResolver __resolver = GROUP_RESOLVER;
    
    public final ProtoModule module;
    public final StringTemplateGroup group;
    public final StringTemplate enumBlockTemplate, messageBlockTemplate, protoBlockTemplate;
    public final boolean javaOutput;
    public final String fileExtension, outputName, outputPrefix, outputSuffix;
    
    public PluginProtoCompiler(ProtoModule module)
    {
        this(module, CHECK_FILENAME_PLACEHOLDER);
    }
    
    public PluginProtoCompiler(ProtoModule module, boolean checkFilenamePlaceHolder)
    {
        this(module ,checkFilenamePlaceHolder, resolveSTG(module));
    }

    public PluginProtoCompiler(ProtoModule module, boolean checkFilenamePlaceHolder, 
            StringTemplateGroup group)
    {
        super(module.getOutput());
        
        this.module = module;
        this.group = group;
        
        protoBlockTemplate = getTemplateFrom(group, "proto_block");
        if(protoBlockTemplate == null)
        {
            // validate that at least enum_block or message_block is present.
            enumBlockTemplate = getTemplateFrom(group, "enum_block");
            messageBlockTemplate = getTemplateFrom(group, "message_block");
            
            if(enumBlockTemplate == null && messageBlockTemplate == null)
            {
                throw new IllegalStateException("At least one of these templates " +
                		"(proto_block|message_block|enum_block) " +
                        "need to be declared in " + module.getOutput());
            }
        }
        else
        {
            enumBlockTemplate = null;
            messageBlockTemplate = null;
        }
        
        fileExtension = getFileExtension(module.getOutput());
        javaOutput = ".java".equalsIgnoreCase(fileExtension);
        
        outputName = getOutputName(module.getOutput());
        
        final int placeHolder = checkFilenamePlaceHolder ? outputName.indexOf('$') : -1;
        if(placeHolder == -1)
        {
            // no placeholder
            outputPrefix = "";
            outputSuffix = "";
        }
        else if(placeHolder == 0)
        {
            // suffix only
            outputPrefix = "";
            // check if provided text is only 1 char "$"
            outputSuffix = outputName.length() == 1 ? "" : outputName.substring(1);
        }
        else if(placeHolder == outputName.length()-1)
        {
            // prefix only
            outputPrefix = outputName.substring(0, outputName.length()-1);
            outputSuffix = "";
        }
        else
        {
            // has both prefix and suffix
            outputPrefix = outputName.substring(0, placeHolder);
            outputSuffix = outputName.substring(placeHolder+1);
        }
    }
    
    /**
     * Returns "foo" from "path/to/foo.java.stg".
     */
    static String getOutputName(String resource)
    {
        final int secondToTheLastDot = resource.lastIndexOf('.', resource.length()-5), 
                slash = resource.lastIndexOf('/', secondToTheLastDot);
        
        return resource.substring(slash+1, secondToTheLastDot);
    }
    
    /**
     * Get the file extension of the provided stg resource.
     */
    public static String getFileExtension(String resource)
    {
        // E.g uf foo.bar.java.stg, it is the . before "java" 
        int secondToTheLastDot = resource.lastIndexOf('.', resource.length()-5);
        if(secondToTheLastDot == -1)
        {
            throw new IllegalArgumentException("The resource must be named like: 'foo.type.stg' " +
            		"where '.type' will be the file extension of the output files.");
        }
        String extension = resource.substring(secondToTheLastDot, resource.length()-4);
        // to protected against resources like "foo..stg"
        if(extension.length() < 2)
        {
            throw new IllegalArgumentException("The resource must be named like: 'foo.type.stg' " +
                        "where '.type' will be the file extension of the output files.");
        }
        
        return extension;
    }
    
    /**
     * Finds the stg resource.
     */
    public static StringTemplateGroup resolveSTG(ProtoModule module)
    {
        return __resolver.resolveSTG(module);
    }
    
    public String resolveFileName(String name)
    {
        return outputPrefix + name + outputSuffix + fileExtension;
    }

    public void compile(ProtoModule module, Proto proto) throws IOException
    {
        if(!this.module.getOutput().startsWith(module.getOutput()))
        {
            throw new IllegalArgumentException("Wrong module: " + 
                    this.module.getOutput() + " != " + module.getOutput());
        }
        
        final String packageName = javaOutput ? proto.getJavaPackageName() : 
            proto.getPackageName();
        
        if(protoBlockTemplate != null)
        {
            compileProtoBlock(module, proto, packageName, protoBlockTemplate);
            return;
        }
        
        if(enumBlockTemplate != null)
        {
            for(EnumGroup eg : proto.getEnumGroups())
            {
                compileEnumBlock(module, eg, packageName, 
                        resolveFileName(eg.getName()), enumBlockTemplate);
            }
        }

        if(messageBlockTemplate != null)
        {
            for(Message message : proto.getMessages())
            {
                compileMessageBlock(module, message, packageName, 
                        resolveFileName(message.getName()), messageBlockTemplate);
            }
        }
    }
    
    public static void compileEnumBlock(ProtoModule module, EnumGroup eg, 
            String packageName, String fileName, 
            StringTemplate enumBlockTemplate) throws IOException
    {
        Writer writer = CompilerUtil.newWriter(module, packageName, fileName);
        
        compileEnumBlockTo(writer, module, eg, enumBlockTemplate);
        
        writer.close();
    }
    
    public static void compileEnumBlockTo(Writer writer, 
            ProtoModule module, EnumGroup eg, 
            StringTemplate enumBlockTemplate) throws IOException
    {
        AutoIndentWriter out = new AutoIndentWriter(writer);
        
        StringTemplate enumBlock = enumBlockTemplate.getInstanceOf();
        enumBlock.setAttribute("eg", eg);
        enumBlock.setAttribute("module", module);
        enumBlock.setAttribute("options", module.getOptions());

        enumBlock.write(out);
    }
    
    public static void compileMessageBlock(ProtoModule module, Message message, 
            String packageName, String fileName, 
            StringTemplate messageBlockTemplate) throws IOException
    {
        Writer writer = CompilerUtil.newWriter(module, packageName, fileName);
        
        compileMessageBlockTo(writer, module, message, messageBlockTemplate);
        
        writer.close();
    }
    
    public static void compileMessageBlockTo(Writer writer, 
            ProtoModule module, Message message, 
            StringTemplate messageBlockTemplate) throws IOException
    {
        AutoIndentWriter out = new AutoIndentWriter(writer);
        
        StringTemplate messageBlock = messageBlockTemplate.getInstanceOf();
        messageBlock.setAttribute("message", message);
        messageBlock.setAttribute("module", module);
        messageBlock.setAttribute("options", module.getOptions());

        messageBlock.write(out);
    }
    
    public void compileProtoBlock(ProtoModule module, Proto proto, 
            String packageName, StringTemplate protoBlockTemplate) throws IOException
    {
        String name = ProtoUtil.toPascalCase(proto.getFile().getName().replaceAll(
                ".proto", "")).toString();
        
        if(javaOutput)
        {
            String outerClassname = proto.getExtraOption("java_outer_classname");
            if(outerClassname != null)
                name = outerClassname;
        }
        
        final String fileName;
        if(outputPrefix.isEmpty() && outputSuffix.isEmpty())
        {
            // resolve the prefix/suffix from module option
            String outerFilePrefix = module.getOption("outer_file_prefix");
            if(outerFilePrefix != null)
                name = outerFilePrefix + name;
            
            String outerFileSuffix = module.getOption("outer_file_suffix");
            if(outerFileSuffix != null)
                name += outerFileSuffix;
            
            fileName = name + fileExtension;
        }
        else
        {
            // use the placeholder in the output name
            fileName = resolveFileName(name);
        }
        
        Writer writer = CompilerUtil.newWriter(module, packageName, fileName);
        
        AutoIndentWriter out = new AutoIndentWriter(writer);
        
        StringTemplate protoBlock = protoBlockTemplate.getInstanceOf();
        protoBlock.setAttribute("proto", proto);
        protoBlock.setAttribute("module", module);
        protoBlock.setAttribute("options", module.getOptions());
        
        protoBlock.write(out);
        writer.close();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy