blob: bb9274b3fcdb82882798258817d240d7d32c2e2c [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package java
import (
"bytes"
"fmt"
"log"
"path"
"v.io/x/ref/lib/vdl/compile"
"v.io/x/ref/lib/vdl/vdlutil"
)
const clientInterfaceTmpl = header + `
// Source: {{ .Source }}
package {{ .PackagePath }};
{{ .ServiceDoc }}
public interface {{ .ServiceName }}Client {{ .Extends }} {
{{ range $method := .Methods }}
{{/* If this method has multiple return arguments, generate the class. */}}
{{ if $method.IsMultipleRet }}
/**
* Multi-return value for method {@link #{{$method.Name}}}.
*/
@io.v.v23.vdl.MultiReturn
public static class {{ $method.UppercaseMethodName }}Out {
{{ range $retArg := $method.RetArgs }}
public {{ $retArg.Type }} {{ $retArg.Name }};
{{ end }}
}
{{ end }}
{{/* Generate the method signature. */}}
{{ $method.Doc }}
{{ if $method.NonStreaming }}
@javax.annotation.CheckReturnValue
{{ end }} {{/* end if $method.NonStreaming */}}
{{ $method.RetType }} {{ $method.Name }}(io.v.v23.context.VContext context{{ $method.Args }});
{{ $method.Doc }}
{{ if $method.NonStreaming }}
@javax.annotation.CheckReturnValue
{{ end }} {{/* end if $method.NonStreaming */}}
{{ $method.RetType }} {{ $method.Name }}(io.v.v23.context.VContext context{{ $method.Args }}, io.v.v23.Options opts);
{{ end }}
}
`
type clientInterfaceArg struct {
Type string
Name string
}
type clientInterfaceMethod struct {
Args string
Doc string
Name string
NonStreaming bool
IsMultipleRet bool
RetArgs []clientInterfaceArg
RetType string
UppercaseMethodName string
}
func clientInterfaceNonStreamingOutArg(iface *compile.Interface, method *compile.Method, useClass bool, env *compile.Env) string {
switch len(method.OutArgs) {
case 0:
// "void" or "Void"
return javaType(nil, useClass, env)
case 1:
return javaType(method.OutArgs[0].Type, useClass, env)
default:
return javaPath(path.Join(interfaceFullyQualifiedName(iface)+"Client", method.Name+"Out"))
}
}
func clientInterfaceStreamingOutArg(iface *compile.Interface, method *compile.Method, env *compile.Env) string {
sendType := javaType(method.InStream, true, env)
recvType := javaType(method.OutStream, true, env)
finishType := clientInterfaceNonStreamingOutArg(iface, method, true, env)
if method.InStream != nil && method.OutStream != nil {
return fmt.Sprintf("io.v.v23.vdl.ClientStream<%s, %s, %s>", sendType, recvType, finishType)
} else if method.InStream != nil {
return fmt.Sprintf("io.v.v23.vdl.ClientSendStream<%s, %s>", sendType, finishType)
} else if method.OutStream != nil {
return fmt.Sprintf("io.v.v23.vdl.ClientRecvStream<%s, %s>", recvType, finishType)
} else {
panic(fmt.Errorf("Streaming method without stream sender and receiver: %v", method))
}
}
func clientInterfaceOutArg(iface *compile.Interface, method *compile.Method, forceClass bool, env *compile.Env) string {
if isStreamingMethod(method) {
return clientInterfaceStreamingOutArg(iface, method, env)
}
return clientInterfaceNonStreamingOutArg(iface, method, forceClass, env)
}
func clientInterfaceRetType(iface *compile.Interface, method *compile.Method, env *compile.Env) string {
if isStreamingMethod(method) {
return clientInterfaceStreamingOutArg(iface, method, env)
}
return "com.google.common.util.concurrent.ListenableFuture<" + clientInterfaceNonStreamingOutArg(iface, method, true, env) + ">"
}
func processClientInterfaceMethod(iface *compile.Interface, method *compile.Method, env *compile.Env) clientInterfaceMethod {
retArgs := make([]clientInterfaceArg, len(method.OutArgs))
for i := 0; i < len(method.OutArgs); i++ {
if method.OutArgs[i].Name != "" {
retArgs[i].Name = vdlutil.FirstRuneToLower(method.OutArgs[i].Name)
} else {
retArgs[i].Name = fmt.Sprintf("ret%d", i+1)
}
retArgs[i].Type = javaType(method.OutArgs[i].Type, false, env)
}
return clientInterfaceMethod{
Args: javaDeclarationArgStr(method.InArgs, env, true),
Doc: javaDoc(method.Doc, method.DocSuffix),
Name: vdlutil.FirstRuneToLower(method.Name),
NonStreaming: !isStreamingMethod(method),
IsMultipleRet: len(retArgs) > 1,
RetArgs: retArgs,
RetType: clientInterfaceRetType(iface, method, env),
UppercaseMethodName: method.Name,
}
}
// genJavaClientInterfaceFile generates the Java interface file for the provided
// interface.
func genJavaClientInterfaceFile(iface *compile.Interface, env *compile.Env) JavaFileInfo {
javaServiceName := vdlutil.FirstRuneToUpper(iface.Name)
methods := make([]clientInterfaceMethod, len(iface.Methods))
for i, method := range iface.Methods {
methods[i] = processClientInterfaceMethod(iface, method, env)
}
data := struct {
FileDoc string
Extends string
Methods []clientInterfaceMethod
PackagePath string
ServiceDoc string
ServiceName string
Source string
}{
FileDoc: iface.File.Package.FileDoc,
Extends: javaClientExtendsStr(iface.Embeds),
Methods: methods,
PackagePath: javaPath(javaGenPkgPath(iface.File.Package.GenPath)),
ServiceDoc: javaDoc(iface.Doc, iface.DocSuffix),
ServiceName: javaServiceName,
Source: iface.File.BaseName,
}
var buf bytes.Buffer
err := parseTmpl("client interface", clientInterfaceTmpl).Execute(&buf, data)
if err != nil {
log.Fatalf("vdl: couldn't execute struct template: %v", err)
}
return JavaFileInfo{
Name: javaServiceName + "Client.java",
Data: buf.Bytes(),
}
}