Package org.waveprotocol.pst

Source Code of org.waveprotocol.pst.Pst

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.waveprotocol.pst;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FileDescriptor;

import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.waveprotocol.pst.model.Message;
import org.waveprotocol.pst.model.MessageProperties;
import org.waveprotocol.pst.style.Styler;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

/**
* PST stands for protobuf-stringtemplate.
*
* The tool allows arbitrary code generation given a series of string
* templates, each passed the description of a protocol buffer file. This allows
* protobuf- based file generation beyond what existing protocol compilers
* (protoc, protostuff, etc) are capable of (without modification), using the
* convenience and safety of <a href="http://stringtemplate.org">string
* template</a>.
*
* A number of sample templates are bundles (in templates), so see these as
* examples. These templates give a complete client/server JSON message stack:
* <ul>
* <li><em>message</em> is a common interface.</li>
* <li><em>messageTestImpl</em> is a simple/pure Java in-memory implementation
* of the interface, for testing.</li>
* <li><em>messagePojoImpl</em> is a like messageTestImpl with JSON
* serialization and deserialization using the Gson library.</li>
* <li><em>messageServerImpl</em> is a protobuf-backed implementation, useful
* for a multi-server environment where the efficient serialization of protocol
* buffers is an advantage. JSON is also supported.</li>
* <li><em>messageClientImpl</em> is an efficent javascript implementation for
* use with GWT.</li>
* </ul>
*
* There is no particular reason why PST can only be used to generate Java, for
* example, a pure JS rather than GWT implementation of the client JSON message
* component could be generated[1].
*
* PST is implemented using the protocol buffer reflection generated by protoc
* alongside the actual Message classes it generates; these are converted into
* simple Java model objects with simple accessors suitable for accessing from
* stringtemplate.
*
* The code generated by stringtemplate is then post-processed using a simple
* custom code formatter since the output from stringtemplate can be hard to
* humanly read (e.g. the indentation is unpredictable).
*
* [1] although, currently it is hardcoded in PST to generate .java files, and
* the model has Java-centric methods.  The code formatter also assumes that
* it is run over a Java file.  These all could be easily modified, however.
*
* @author kalman@google.com (Benjamin Kalman)
*/
public final class Pst {

  private final File outputDir;
  private final FileDescriptor fd;
  private final Styler styler;
  private final Iterable<File> templates;
  private final boolean saveBackups;
  private final boolean useInt52;

  /**
   * @param outputDir the base directory to write the generated files
   * @param fd the {@link FileDescriptor} of the protobuf to use (i.e. pass to
   *        each string template)
   * @param styler the code styler to post-process generated code with
   * @param templates the collection of string templates to use
   * @param saveBackups whether to save intermediate generated files
   * @param useInt52 whether we use doubles to serialize 64-bit integers
   */
  public Pst(File outputDir, FileDescriptor fd, Styler styler, Iterable<File> templates,
      boolean saveBackups, boolean useInt52) {
    this.outputDir = checkNotNull(outputDir, "outputDir cannot be null");
    this.fd = checkNotNull(fd, "fd cannot be null");
    this.styler = checkNotNull(styler, "styler cannot be null");
    this.templates = checkNotNull(templates, "templates cannot be null");
    this.saveBackups = saveBackups;
    this.useInt52 = useInt52;
  }

  /**
   * Runs the code generation for all templates.
   */
  public void run() throws PstException {
    List<PstException.TemplateException> exceptions = Lists.newArrayList();
    for (File template : templates) {
      try {
        MessageProperties properties = createProperties(template);
        String groupName = stripSuffix(".st", template.getName());
        String templateName = properties.hasTemplateName() ?
            properties.getTemplateName() : "";
        StringTemplateGroup group = new StringTemplateGroup(groupName + "Group", dir(template));
        StringTemplate st = group.getInstanceOf(groupName);
        for (Descriptor messageDescriptor : fd.getMessageTypes()) {
          Message message = new Message(messageDescriptor, templateName, properties);
          st.reset();
          st.setAttribute("m", message);
          write(st, new File(
              outputDir.getPath() + File.separator +
              message.getFullJavaType().replace('.', File.separatorChar) + "." +
              (properties.hasFileExtension() ? properties.getFileExtension() : "java")));
        }
      } catch (Exception e) {
        exceptions.add(new PstException.TemplateException(template.getPath(), e));
      }
    }
    if (!exceptions.isEmpty()) {
      throw new PstException(exceptions);
    }
  }

  /**
   * @return the path to the directory which contains a file, or just the path
   *         to the file itself if it's already a directory
   */
  private String dir(File f) {
    return f.isDirectory()
        ? f.getPath()
        : (Strings.isNullOrEmpty(f.getParent()) ? "." : f.getParent());
  }

  private String stripSuffix(String suffix, String s) {
    return s.endsWith(suffix) ? s.substring(0, s.length() - suffix.length()) : s;
  }

  private void write(StringTemplate st, File output) throws IOException {
    output.getParentFile().mkdirs();
    BufferedWriter writer = new BufferedWriter(new FileWriter(output));
    try {
      writer.write(st.toString());
    } finally {
      try {
        writer.close();
      } catch (IOException e) {
        // If another exception is already propagating, we don't
        // want to throw a secondary one.
        // This means that exceptions on close() are ignored,
        // but what could usefully be done for a close()
        // exception anyway?
      }
    }
    styler.style(output, saveBackups);
  }

  private MessageProperties createProperties(File template)
      throws FileNotFoundException, IOException {
    File propertiesFile =
        new File(template.getParentFile().getPath() + File.separator + "properties");
    MessageProperties properties = propertiesFile.exists()
        ? MessageProperties.createFromFile(propertiesFile) : MessageProperties.createEmpty();
    properties.setUseInt52(useInt52);
    return properties;
  }

}
TOP

Related Classes of org.waveprotocol.pst.Pst

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.