| // 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(), |
| } |
| } |