/*
* 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.apache.thrift.maven;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import java.io.File;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newLinkedList;
import static com.google.common.collect.Sets.newHashSet;
/**
* This class represents an invokable configuration of the {@code thrift}
* compiler. The actual executable is invoked using the plexus
* {@link Commandline}.
*
* This class currently only supports generating java source files.
*/
final class Thrift {
final static String GENERATED_JAVA = "gen-java";
private final String executable;
private final String generator;
private final ImmutableSet thriftPathElements;
private final ImmutableSet thriftFiles;
private final File javaOutputDirectory;
private final CommandLineUtils.StringStreamConsumer output;
private final CommandLineUtils.StringStreamConsumer error;
/**
* Constructs a new instance. This should only be used by the {@link Builder}.
*
* @param executable The path to the {@code thrift} executable.
* @param generator The value for the {@code --gen} option.
* @param thriftPath The directories in which to search for imports.
* @param thriftFiles The thrift source files to compile.
* @param javaOutputDirectory The directory into which the java source files
* will be generated.
*/
private Thrift(String executable, String generator, ImmutableSet thriftPath,
ImmutableSet thriftFiles, File javaOutputDirectory) {
this.executable = checkNotNull(executable, "executable");
this.generator = checkNotNull(generator, "generator");
this.thriftPathElements = checkNotNull(thriftPath, "thriftPath");
this.thriftFiles = checkNotNull(thriftFiles, "thriftFiles");
this.javaOutputDirectory = checkNotNull(javaOutputDirectory, "javaOutputDirectory");
this.error = new CommandLineUtils.StringStreamConsumer();
this.output = new CommandLineUtils.StringStreamConsumer();
}
/**
* Invokes the {@code thrift} compiler using the configuration specified at
* construction.
*
* @return The exit status of {@code thrift}.
* @throws CommandLineException
*/
public int compile() throws CommandLineException {
for (File thriftFile : thriftFiles) {
Commandline cl = new Commandline();
cl.setExecutable(executable);
cl.addArguments(buildThriftCommand(thriftFile).toArray(new String[]{}));
final int result = CommandLineUtils.executeCommandLine(cl, null, output, error);
if (result != 0) {
return result;
}
}
// result will always be 0 here.
return 0;
}
/**
* Creates the command line arguments.
*
* This method has been made visible for testing only.
*
* @param thriftFile
* @return A list consisting of the executable followed by any arguments.
*/
ImmutableList buildThriftCommand(final File thriftFile) {
final List command = newLinkedList();
// add the executable
for (File thriftPathElement : thriftPathElements) {
command.add("-I");
command.add(thriftPathElement.toString());
}
command.add("-out");
command.add(javaOutputDirectory.toString());
command.add("--gen");
command.add(generator);
command.add(thriftFile.toString());
return ImmutableList.copyOf(command);
}
/**
* @return the output
*/
public String getOutput() {
return output.getOutput();
}
/**
* @return the error
*/
public String getError() {
return error.getOutput();
}
/**
* This class builds {@link Thrift} instances.
*/
static final class Builder {
private final String executable;
private final File javaOutputDirectory;
private Set thriftPathElements;
private Set thriftFiles;
private String generator;
/**
* Constructs a new builder. The two parameters are present as they are
* required for all {@link Thrift} instances.
*
* @param executable The path to the {@code thrift} executable.
* @param javaOutputDirectory The directory into which the java source files
* will be generated.
* @throws NullPointerException If either of the arguments are {@code null}.
* @throws IllegalArgumentException If the {@code javaOutputDirectory} is
* not a directory.
*/
public Builder(String executable, File javaOutputDirectory) {
this.executable = checkNotNull(executable, "executable");
this.javaOutputDirectory = checkNotNull(javaOutputDirectory);
checkArgument(javaOutputDirectory.isDirectory());
this.thriftFiles = newHashSet();
this.thriftPathElements = newHashSet();
}
/**
* Adds a thrift file to be compiled. Thrift files must be on the thriftpath
* and this method will fail if a thrift file is added without first adding a
* parent directory to the thriftpath.
*
* @param thriftFile
* @return The builder.
* @throws IllegalStateException If a thrift file is added without first
* adding a parent directory to the thriftpath.
* @throws NullPointerException If {@code thriftFile} is {@code null}.
*/
public Builder addThriftFile(File thriftFile) {
checkNotNull(thriftFile);
checkArgument(thriftFile.isFile());
checkArgument(thriftFile.getName().endsWith(".thrift"));
checkThriftFileIsInThriftPath(thriftFile);
thriftFiles.add(thriftFile);
return this;
}
/**
* Adds the option string for the Thrift executable's {@code --gen} parameter.
*
* @param generator
* @return The builder
* @throws NullPointerException If {@code generator} is {@code null}.
*/
public Builder setGenerator(String generator) {
checkNotNull(generator);
this.generator = generator;
return this;
}
private void checkThriftFileIsInThriftPath(File thriftFile) {
assert thriftFile.isFile();
checkState(checkThriftFileIsInThriftPathHelper(thriftFile.getParentFile()));
}
private boolean checkThriftFileIsInThriftPathHelper(File directory) {
assert directory.isDirectory();
if (thriftPathElements.contains(directory)) {
return true;
} else {
final File parentDirectory = directory.getParentFile();
return (parentDirectory == null) ? false
: checkThriftFileIsInThriftPathHelper(parentDirectory);
}
}
/**
* @see #addThriftFile(File)
*/
public Builder addThriftFiles(Iterable thriftFiles) {
for (File thriftFile : thriftFiles) {
addThriftFile(thriftFile);
}
return this;
}
/**
* Adds the {@code thriftPathElement} to the thriftPath.
*
* @param thriftPathElement A directory to be searched for imported thrift message
* buffer definitions.
* @return The builder.
* @throws NullPointerException If {@code thriftPathElement} is {@code null}.
* @throws IllegalArgumentException If {@code thriftPathElement} is not a
* directory.
*/
public Builder addThriftPathElement(File thriftPathElement) {
checkNotNull(thriftPathElement);
checkArgument(thriftPathElement.isDirectory());
thriftPathElements.add(thriftPathElement);
return this;
}
/**
* @see #addThriftPathElement(File)
*/
public Builder addThriftPathElements(Iterable thriftPathElements) {
for (File thriftPathElement : thriftPathElements) {
addThriftPathElement(thriftPathElement);
}
return this;
}
/**
* @return A configured {@link Thrift} instance.
* @throws IllegalStateException If no thrift files have been added.
*/
public Thrift build() {
checkState(!thriftFiles.isEmpty());
return new Thrift(executable, generator, ImmutableSet.copyOf(thriftPathElements),
ImmutableSet.copyOf(thriftFiles), javaOutputDirectory);
}
}
}