318 lines
14 KiB
C#
318 lines
14 KiB
C#
/*
|
||
* java.cs
|
||
*
|
||
* This source file is part of the FoundationDB open source project
|
||
*
|
||
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
|
||
*
|
||
* 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.
|
||
*/
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Text;
|
||
|
||
namespace vexillographer
|
||
{
|
||
class java : BindingWriter
|
||
{
|
||
private static string formatComment(int indentTabs, string commentText)
|
||
{
|
||
string tabs = "";
|
||
for(int i=0; i<indentTabs; i++) tabs += "\t";
|
||
return tabs + "/**" +
|
||
string.Join( "",
|
||
commentText.Split('\n')
|
||
.Select( line=> "\n" + tabs + " * " + line.Trim() ) )
|
||
+ "\n" + tabs + " *" + "/";
|
||
}
|
||
|
||
private static string replaceTicks(string input)
|
||
{
|
||
int startIdx = input.IndexOf("``");
|
||
int lastReplaceEnd = 0;
|
||
string output = "";
|
||
while (startIdx >= 0)
|
||
{
|
||
output += input.Substring(lastReplaceEnd, startIdx - lastReplaceEnd);
|
||
int closingIdx = input.IndexOf("``", startIdx + 2);
|
||
if (closingIdx < 0)
|
||
throw new InvalidDataException("No closing double tick");
|
||
output += "{@code " + input.Substring(startIdx + 2, closingIdx - startIdx - 2) + "}";
|
||
lastReplaceEnd = closingIdx + 2;
|
||
startIdx = input.IndexOf("``", lastReplaceEnd);
|
||
}
|
||
output += input.Substring(lastReplaceEnd);
|
||
return output;
|
||
}
|
||
|
||
private static string getEnum(Option o)
|
||
{
|
||
string mainComment = o.comment == "" ? "Currently undocumented." : o.comment;
|
||
if (!mainComment.EndsWith("."))
|
||
mainComment += ".";
|
||
mainComment = replaceTicks(mainComment);
|
||
return formatComment(1, mainComment) +
|
||
String.Format("\n{2}\t{0}({1})", o.name.ToUpper(), o.code, o.isDeprecated() ? "\t@Deprecated\n" : "");
|
||
}
|
||
|
||
class ScopeOptions
|
||
{
|
||
public bool isPublic;
|
||
public bool isSettableOption;
|
||
public String comment;
|
||
|
||
public ScopeOptions(bool isSettable, String comment, bool isPublic = true) {
|
||
this.isPublic = isPublic;
|
||
this.isSettableOption = isSettable;
|
||
this.comment = comment;
|
||
}
|
||
}
|
||
|
||
private static Dictionary<Scope, ScopeOptions> scopeDocOptions = new Dictionary<Scope, ScopeOptions>()
|
||
{
|
||
{ Scope.NetworkOption, new ScopeOptions(true,
|
||
"A set of options that can be set globally for the {@link FDB FoundationDB API}.") },
|
||
{ Scope.DatabaseOption, new ScopeOptions(true,
|
||
"A set of options that can be set on a {@link Database}.") },
|
||
{ Scope.TransactionOption, new ScopeOptions(true,
|
||
"A set of options that can be set on a {@link Transaction}.") },
|
||
{ Scope.StreamingMode, new ScopeOptions(false,
|
||
"Options that control the way the Java binding performs range reads. These options can be passed to {@link Transaction#getRange(byte[], byte[], int, boolean, StreamingMode) Transaction.getRange(...)}.") },
|
||
{ Scope.MutationType, new ScopeOptions(false,
|
||
"A set of operations that can be performed atomically on a database. These are used as parameters to {@link Transaction#mutate(MutationType, byte[], byte[])}.") },
|
||
{ Scope.ConflictRangeType, new ScopeOptions(false,
|
||
"Conflict range types used internally by the C API.", false) },
|
||
{ Scope.ErrorPredicate, new ScopeOptions(true,
|
||
"Error code predicates for binding writers and non-standard layer implementers.") },
|
||
};
|
||
|
||
private static bool scopeIsSettableOption(Scope s) {
|
||
return s != Scope.StreamingMode && s != Scope.MutationType && s != Scope.ConflictRangeType;
|
||
}
|
||
|
||
private static string toCamelCase(string optionName) {
|
||
return string.Join("",
|
||
optionName
|
||
.Split('_')
|
||
.Select( s=>s.Substring(0,1).ToUpper() + s.Substring(1) )
|
||
);
|
||
}
|
||
|
||
private static string toSetFuncName(string optionName) {
|
||
return "set" + toCamelCase(optionName);
|
||
}
|
||
|
||
private static string toPredicateFuncName(string optionName) {
|
||
return "is" + toCamelCase(optionName);
|
||
}
|
||
|
||
private static string getJavaTypeName(ParamType t)
|
||
{
|
||
switch (t)
|
||
{
|
||
case ParamType.Int: return "long";
|
||
case ParamType.Bytes: return "byte[]";
|
||
case ParamType.String: return "String";
|
||
default: throw new InvalidDataException("Unsupported operation type");
|
||
}
|
||
}
|
||
|
||
private static void writeOptionsClass(TextWriter outFile, Scope scope, IEnumerable<Option> options)
|
||
{
|
||
string className = scope.ToString() + "s";
|
||
outFile.WriteLine( "package com.apple.foundationdb;" );
|
||
outFile.WriteLine();
|
||
|
||
outFile.WriteLine(formatComment(0, scopeDocOptions[scope].comment +
|
||
(options.Count() == 0 ? "\n\nThere are currently no options available." : "")
|
||
));
|
||
outFile.WriteLine("public class {0} extends OptionsSet {{", className);
|
||
outFile.WriteLine("\tpublic {0}( OptionConsumer consumer ) {{ super(consumer); }}", className);
|
||
foreach(var option in options) {
|
||
if (!option.hidden) {
|
||
outFile.WriteLine();
|
||
if (option.comment != "")
|
||
{
|
||
string comment = option.comment;
|
||
if (!comment.EndsWith("."))
|
||
comment += ".";
|
||
if (option.paramDesc != null)
|
||
comment += "\n\n@param value " + option.paramDesc;
|
||
outFile.WriteLine(formatComment(1, replaceTicks(comment)));
|
||
}
|
||
if (option.isDeprecated())
|
||
outFile.WriteLine("\t@Deprecated");
|
||
if (option.paramType == ParamType.None)
|
||
outFile.WriteLine("\tpublic void {0}() {{ setOption({1}); }}", toSetFuncName(option.name), option.code);
|
||
else
|
||
outFile.WriteLine("\tpublic void {0}({2} value) {{ setOption({1}, value); }}", toSetFuncName(option.name), option.code, getJavaTypeName(option.paramType));
|
||
}
|
||
}
|
||
outFile.WriteLine("}");
|
||
}
|
||
|
||
private static void writePredicateClass(TextWriter outFile, Scope scope, IEnumerable<Option> options)
|
||
{
|
||
outFile.WriteLine(
|
||
@"package com.apple.foundationdb;
|
||
|
||
import com.apple.foundationdb.async.CloneableException;
|
||
|
||
/**
|
||
* An Error from the native layers of FoundationDB. Each {@code FDBException} sets
|
||
* the {@code message} of the underlying Java {@link Exception}. FDB exceptions expose
|
||
* a number of functions including, for example, {@link #isRetryable()} that
|
||
* evaluate predicates on the internal FDB error. Most clients should use those methods
|
||
* in order to implement special handling for certain errors if their application
|
||
* requires it.
|
||
*
|
||
* <p>
|
||
* Errors in FDB should generally be retried if they match the {@link #isRetryable()}
|
||
* predicate. In addition, as with any distributed system, certain classes of errors
|
||
* may fail in such a way that it is unclear whether the transaction succeeded (they
|
||
* {@link #isMaybeCommitted() may be committed} or not). To handle these cases, clients
|
||
* are generally advised to make their database operations idempotent and to place
|
||
* their operations within retry loops. The FDB Java API provides some default retry loops
|
||
* within the {@link Database} interface. See the discussion within the documentation of
|
||
* {@link Database#runAsync(Function) Database.runAsync()} for more details.
|
||
*
|
||
* @see com.apple.foundationdb.Transaction#onError(Throwable) Transaction.onError()
|
||
* @see com.apple.foundationdb.Database#runAsync(Function) Database.runAsync()
|
||
*/
|
||
public class FDBException extends RuntimeException implements CloneableException {
|
||
private static final long serialVersionUID = 1L;
|
||
private final int code;
|
||
|
||
/**
|
||
* A general constructor. Not for use by client code.
|
||
*
|
||
* @param message error message of this exception
|
||
* @param code internal FDB error code of this exception
|
||
*/
|
||
public FDBException(String message, int code) {
|
||
super(message);
|
||
this.code = code;
|
||
}
|
||
|
||
/**
|
||
* Gets the code for this error. A list of common errors codes
|
||
* are published <a href=""/foundationdb/api-error-codes.html"">elsewhere within
|
||
* our documentation</a>.
|
||
*
|
||
* @return the internal FDB error code
|
||
*/
|
||
public int getCode() {
|
||
return code;
|
||
}
|
||
|
||
/**
|
||
* Determine if this {@code FDBException} represents a success code from the native layer.
|
||
*
|
||
* @return {@code true} if this error represents success, {@code false} otherwise
|
||
*/
|
||
public boolean isSuccess() {
|
||
return getCode() == 0;
|
||
}
|
||
|
||
@Override
|
||
public Exception retargetClone() {
|
||
FDBException exception = new FDBException(getMessage(), code);
|
||
exception.initCause(this);
|
||
return exception;
|
||
}
|
||
");
|
||
|
||
foreach(var option in options) {
|
||
if (!option.hidden) {
|
||
outFile.WriteLine();
|
||
if (option.comment != "")
|
||
{
|
||
string comment = option.comment;
|
||
if (!comment.EndsWith("."))
|
||
comment += ".";
|
||
if (option.paramDesc != null)
|
||
comment += "\n\n@param value " + option.paramDesc;
|
||
comment += "\n\n@return {@code true} if this {@code FDBException} is {@code " + option.name + "}";
|
||
outFile.WriteLine(formatComment(1, replaceTicks(comment)));
|
||
}
|
||
if (option.isDeprecated())
|
||
outFile.WriteLine("\t@Deprecated");
|
||
outFile.WriteLine("\tpublic boolean {0}() {{ return FDB.evalErrorPredicate({1}, this.code); }}", toPredicateFuncName(option.name), option.code);
|
||
}
|
||
}
|
||
outFile.WriteLine("}");
|
||
}
|
||
|
||
private static void writeEnumClass(TextWriter outFile, Scope scope, IEnumerable<Option> options)
|
||
{
|
||
string scopeName = scope.ToString();
|
||
|
||
outFile.WriteLine( "package com.apple.foundationdb;" );
|
||
outFile.WriteLine();
|
||
outFile.WriteLine(formatComment(0, scopeDocOptions[scope].comment +
|
||
(options.Count() == 0 ? "\n\nThere are currently no options available." : "")));
|
||
|
||
outFile.WriteLine("{0}enum {1} {{", scopeDocOptions[scope].isPublic ? "public " : "", scopeName);
|
||
outFile.WriteLine(string.Join(",\n\n", options.Where(f => !f.hidden).Select(f => getEnum(f)).ToArray()) + ";");
|
||
outFile.WriteLine(
|
||
@"
|
||
private final int code;
|
||
|
||
{0}(int code) {{
|
||
this.code = code;
|
||
}}
|
||
|
||
/**
|
||
* Gets the FoundationDB native-level constant code for a {{@code {0}}}.
|
||
*
|
||
* @return the native code for a FoundationDB {{@code {0}}} constant.
|
||
*/
|
||
public int code() {{
|
||
return this.code;
|
||
}}
|
||
}}", scopeName);
|
||
}
|
||
|
||
public void writeFiles(string outputDirectory, IEnumerable<Option> options)
|
||
{
|
||
foreach (Scope s in Enum.GetValues(typeof(Scope)))
|
||
{
|
||
if (!Directory.Exists(outputDirectory))
|
||
throw new Exception(string.Format("Directory {0} does not exist", outputDirectory));
|
||
|
||
string className = s.ToString();
|
||
if (scopeDocOptions[s].isSettableOption) className += "s";
|
||
string filePath = Path.Combine(outputDirectory, (s == Scope.ErrorPredicate ? "FDBException" : className) + ".java");
|
||
using (var javaFile = System.IO.File.Open(filePath,
|
||
System.IO.FileMode.Create, System.IO.FileAccess.Write))
|
||
{
|
||
TextWriter outFile = new StreamWriter(javaFile);
|
||
outFile.NewLine = "\r";
|
||
if (scopeDocOptions[s].isSettableOption) {
|
||
if(s == Scope.ErrorPredicate)
|
||
writePredicateClass(outFile, s, options.Where(o => o.scope == s).OrderBy(o=>o.comment==""));
|
||
else
|
||
writeOptionsClass(outFile, s, options.Where(o => o.scope == s).OrderBy(o=>o.comment==""));
|
||
}
|
||
else
|
||
writeEnumClass(outFile, s, options.Where(o => o.scope == s));
|
||
outFile.Flush();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|