2017-05-26 04:48:44 +08:00
/ *
* java . cs
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013 - 2018 Apple Inc . and the FoundationDB project authors
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* 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
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* http : //www.apache.org/licenses/LICENSE-2.0
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* 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" ;
2017-10-16 23:31:44 +08:00
outFile . WriteLine ( "package com.apple.foundationdb;" ) ;
2017-05-26 04:48:44 +08:00
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 (
2017-10-16 23:31:44 +08:00
@ "package com.apple.foundationdb;
2017-05-26 04:48:44 +08:00
2017-10-16 23:31:44 +08:00
import com . apple . foundationdb . async . CloneableException ;
2017-05-26 04:48:44 +08:00
/ * *
* An Error from the native layers of FoundationDB . Each { @code FDBException } sets
2018-03-20 09:45:21 +08:00
* 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 .
2017-05-26 04:48:44 +08:00
*
2018-03-20 09:45:21 +08:00
* < 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
2018-03-22 10:25:36 +08:00
* within the { @link Database } interface . See the discussion within the documentation of
2018-03-20 09:45:21 +08:00
* { @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 ( )
2017-05-26 04:48:44 +08:00
* /
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 .
2018-03-20 09:45:21 +08:00
*
* @param message error message of this exception
* @param code internal FDB error code of this exception
2017-05-26 04:48:44 +08:00
* /
public FDBException ( String message , int code ) {
super ( message ) ;
this . code = code ;
}
/ * *
2018-03-20 09:45:21 +08:00
* Gets the code for this error . A list of common errors codes
2018-04-20 00:44:40 +08:00
* are published < a href = "" / foundationdb / api - error - codes . html "" > elsewhere within
2018-03-20 09:45:21 +08:00
* our documentation < / a > .
*
* @return the internal FDB error code
2017-05-26 04:48:44 +08:00
* /
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 ;
2018-03-20 09:45:21 +08:00
comment + = "\n\n@return {@code true} if this {@code FDBException} is {@code " + option . name + "}" ;
2017-05-26 04:48:44 +08:00
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 ( ) ;
2017-10-16 23:31:44 +08:00
outFile . WriteLine ( "package com.apple.foundationdb;" ) ;
2017-05-26 04:48:44 +08:00
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 ( ) ;
}
}
}
}
}