blob: 472f47b94bff31b51ff4debcfbe3040a36b6fc4b [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 (
"fmt"
"strconv"
"strings"
"v.io/v23/vdl"
"v.io/x/ref/lib/vdl/compile"
)
const javaMaxStringLen = 2 << 16 // 64k
// javaConstVal returns the value string for the provided constant value.
func javaConstVal(v *vdl.Value, env *compile.Env) (ret string) {
if v == nil {
return "null"
}
if v.IsZero() {
return javaZeroValue(v.Type(), env)
}
ret = javaVal(v, env)
switch v.Type().Kind() {
case vdl.Enum, vdl.Union, vdl.Int8, vdl.Uint16, vdl.Uint32, vdl.Uint64:
return
}
if def := env.FindTypeDef(v.Type()); def != nil && def.File != compile.BuiltInFile { // User-defined type.
ret = fmt.Sprintf("new %s(%s)", javaType(v.Type(), false, env), ret)
}
return
}
// javaVal returns the value string for the provided Value.
func javaVal(v *vdl.Value, env *compile.Env) string {
const longSuffix = "L"
const floatSuffix = "f"
if v.Kind() == vdl.Array || (v.Kind() == vdl.List && v.Type().Elem().Kind() == vdl.Byte && v.Type().Name() == "") {
ret := fmt.Sprintf("new %s[] {", javaType(v.Type().Elem(), false, env))
for i := 0; i < v.Len(); i++ {
if i > 0 {
ret = ret + ", "
}
ret = ret + javaConstVal(v.Index(i), env)
}
return ret + "}"
}
switch v.Kind() {
case vdl.Bool:
if v.Bool() {
return "true"
} else {
return "false"
}
case vdl.Byte:
return "(byte)0x" + strconv.FormatUint(v.Uint(), 16)
case vdl.Int8:
return fmt.Sprintf("new %s((byte) %s)", javaType(v.Type(), true, env), strconv.FormatInt(v.Int(), 10))
case vdl.Uint16:
return fmt.Sprintf("new %s((short) %s)", javaType(v.Type(), true, env), strconv.FormatUint(v.Uint(), 10))
case vdl.Int16:
return "(short)" + strconv.FormatInt(v.Int(), 10)
case vdl.Uint32:
return fmt.Sprintf("new %s((int) %s)", javaType(v.Type(), true, env), strconv.FormatUint(v.Uint(), 10)+longSuffix)
case vdl.Int32:
return strconv.FormatInt(v.Int(), 10)
case vdl.Uint64:
// Note: this is formatting the number as an int because Java will error on unsigned contants that use the
// full 64-bits.
return fmt.Sprintf("new %s(%s)", javaType(v.Type(), true, env), strconv.FormatInt(int64(v.Uint()), 10)+longSuffix)
case vdl.Int64:
return strconv.FormatInt(v.Int(), 10) + longSuffix
case vdl.Float32, vdl.Float64:
c := strconv.FormatFloat(v.Float(), 'g', -1, bitlen(v.Kind()))
if strings.Index(c, ".") == -1 {
c += ".0"
}
if v.Kind() == vdl.Float32 {
return c + floatSuffix
}
return c
case vdl.String:
in := v.RawString()
if len(in) < javaMaxStringLen {
return strconv.Quote(in)
}
// Java is unable to handle constant strings larger than 64k so split them into multiple strings.
out := "String.join(\"\", new java.util.ArrayList<CharSequence>("
for len(in) > javaMaxStringLen {
out += strconv.Quote(in[:javaMaxStringLen]) + ","
in = in[javaMaxStringLen:]
}
return out + strconv.Quote(in) + "))"
case vdl.Any:
if v.Elem() == nil {
return fmt.Sprintf("new %s()", javaType(v.Type(), false, env))
}
elemReflectTypeStr := javaReflectType(v.Elem().Type(), env)
elemStr := javaConstVal(v.Elem(), env)
return fmt.Sprintf("new %s(%s, %s)", javaType(v.Type(), false, env), elemReflectTypeStr, elemStr)
case vdl.Enum:
return fmt.Sprintf("%s.%s", javaType(v.Type(), false, env), v.EnumLabel())
case vdl.List:
elemTypeStr := javaType(v.Type().Elem(), true, env)
ret := fmt.Sprintf("new com.google.common.collect.ImmutableList.Builder<%s>()", elemTypeStr)
for i := 0; i < v.Len(); i++ {
ret = fmt.Sprintf("%s\n.add(%s)", ret, javaConstVal(v.Index(i), env))
}
return ret + ".build()"
case vdl.Map:
keyTypeStr := javaType(v.Type().Key(), true, env)
elemTypeStr := javaType(v.Type().Elem(), true, env)
ret := fmt.Sprintf("new com.google.common.collect.ImmutableMap.Builder<%s, %s>()", keyTypeStr, elemTypeStr)
for _, key := range vdl.SortValuesAsString(v.Keys()) {
keyStr := javaConstVal(key, env)
elemStr := javaConstVal(v.MapIndex(key), env)
ret = fmt.Sprintf("%s\n.put(%s, %s)", ret, keyStr, elemStr)
}
return ret + ".build()"
case vdl.Union:
index, value := v.UnionField()
name := v.Type().Field(index).Name
elemStr := javaConstVal(value, env)
return fmt.Sprintf("new %s.%s(%s)", javaType(v.Type(), false, env), name, elemStr)
case vdl.Set:
keyTypeStr := javaType(v.Type().Key(), true, env)
ret := fmt.Sprintf("new com.google.common.collect.ImmutableSet.Builder<%s>()", keyTypeStr)
for _, key := range vdl.SortValuesAsString(v.Keys()) {
ret = fmt.Sprintf("%s\n.add(%s)", ret, javaConstVal(key, env))
}
return ret + ".build()"
case vdl.Struct:
var ret string
for i := 0; i < v.Type().NumField(); i++ {
if i > 0 {
ret = ret + ", "
}
ret = ret + javaConstVal(v.StructField(i), env)
}
return ret
case vdl.TypeObject:
return fmt.Sprintf("new %s(%s)", javaType(v.Type(), false, env), javaReflectType(v.TypeObject(), env))
case vdl.Optional:
if v.Elem() != nil {
return fmt.Sprintf("io.v.v23.vdl.VdlOptional.of(%s)", javaConstVal(v.Elem(), env))
} else {
return fmt.Sprintf("new %s(%s)", javaType(v.Type(), false, env), javaReflectType(v.Type(), env))
}
}
panic(fmt.Errorf("vdl: javaVal unhandled type %v %v", v.Kind(), v.Type()))
}
// javaZeroValue returns the zero value string for the provided VDL value.
// We assume that default constructor of user-defined types returns a zero value.
func javaZeroValue(t *vdl.Type, env *compile.Env) string {
if _, ok := javaNativeType(t, env); ok {
return "null"
}
// First process user-defined types.
switch t.Kind() {
case vdl.Enum:
return fmt.Sprintf("%s.%s", javaType(t, false, env), t.EnumLabel(0))
case vdl.Union:
return fmt.Sprintf("new %s.%s()", javaType(t, false, env), t.Field(0).Name)
}
if def := env.FindTypeDef(t); def != nil && def.File != compile.BuiltInFile {
return fmt.Sprintf("new %s()", javaType(t, false, env))
}
// Arrays, enums, structs and unions can be user-defined only.
if t.Kind() == vdl.List && t.Elem().Kind() == vdl.Byte {
return fmt.Sprintf("new %s[]{}", javaType(t.Elem(), false, env))
}
switch t.Kind() {
case vdl.Bool:
return "false"
case vdl.Byte:
return "(byte) 0"
case vdl.Int16:
return "(short) 0"
case vdl.Int32:
return "0"
case vdl.Int64:
return "0L"
case vdl.Float32:
return "0.0f"
case vdl.Float64:
return "0.0"
case vdl.Any, vdl.TypeObject, vdl.Int8, vdl.Uint16, vdl.Uint32, vdl.Uint64:
return fmt.Sprintf("new %s()", javaType(t, false, env))
case vdl.String:
return "\"\""
case vdl.List:
return fmt.Sprintf("new java.util.ArrayList<%s>()", javaType(t.Elem(), true, env))
case vdl.Map:
keyTypeStr := javaType(t.Key(), true, env)
elemTypeStr := javaType(t.Elem(), true, env)
return fmt.Sprintf("new java.util.HashMap<%s, %s>()", keyTypeStr, elemTypeStr)
case vdl.Set:
return fmt.Sprintf("new java.util.HashSet<%s>()", javaType(t.Key(), true, env))
case vdl.Optional:
return fmt.Sprintf("new %s(%s)", javaType(t, false, env), javaReflectType(t, env))
}
panic(fmt.Errorf("vdl: javaZeroValue unhandled type %v", t))
}