meta/signature/type.go

Summary

Maintainability
F
4 days
Test Coverage
B
84%
package signature

import (
    "bytes"
    "fmt"
    "log"
    "reflect"
    "strings"

    "github.com/dave/jennifer/jen"
)

// TypeSet is a container which contains exactly one instance of each
// Type currently generated. It is used to generate the
// type declaration only once.
type TypeSet struct {
    Names []string
    Types []Type
}

// Declare writes all the registered Type into the jen.File.
func (s *TypeSet) Declare(f *jen.File) {
    for _, v := range s.Types {
        v.TypeDeclaration(f)
    }
}

// Search returns a Type if a name is associated.
func (s *TypeSet) Search(name string) Type {
    for i, n := range s.Names {
        if n == name {
            return s.Types[i]
        }
    }
    return nil
}

// ResolveCollision returns a type name not already present in the
// set. If name if not present, returns name, else it returns a new
// string derived from name.
func (s *TypeSet) ResolveCollision(originalName, signature string) string {
    name := originalName
    // loop 100 times to avoid name collision
    for i := 0; i < 100; i++ {
        ok := true // can use the name
        for i, n := range s.Names {
            if n == name {
                if s.Types[i].Signature() == signature {
                    // already registered
                    return name
                }
                ok = false
                break
            }
        }
        if ok {
            return name
        }
        name = fmt.Sprintf("%s_%d", originalName, i)
    }
    return "can_not_register_name_" + originalName
}

// NewTypeSet construct a new TypeSet.
func NewTypeSet() *TypeSet {
    typ := make([]Type, 0)
    nam := make([]string, 0)
    return &TypeSet{nam, typ}
}

// Type represents a type of a signature or a type
// embedded inside a signature. Type represents types for
// primitive types (int, long, float, string), vectors of a type,
// associative maps and structures.
type Type interface {
    Signature() string
    SignatureIDL() string
    TypeName() *Statement
    TypeDeclaration(*jen.File)
    RegisterTo(s *TypeSet)
    Marshal(id string, writer string) *Statement // returns an error
    Unmarshal(reader string) *Statement          // returns (type, err)
    Reader() TypeReader
    Type() reflect.Type
}

type typeConstructor struct {
    signature    string
    signatureIDL string
    typeName     *Statement
    marshal      func(id string, writer string) *Statement // returns an error
    unmarshal    func(reader string) *Statement            // returns (type, err)
    reader       TypeReader
    typ          reflect.Type
}

func (t *typeConstructor) Signature() string {
    return t.signature
}
func (t *typeConstructor) SignatureIDL() string {
    return t.signatureIDL
}
func (t *typeConstructor) TypeName() *Statement {
    return t.typeName
}
func (t *typeConstructor) TypeDeclaration(f *jen.File) {
    return
}
func (t *typeConstructor) RegisterTo(s *TypeSet) {
    return
}
func (t *typeConstructor) Marshal(id string, writer string) *Statement {
    return t.marshal(id, writer)
}
func (t *typeConstructor) Unmarshal(reader string) *Statement {
    return t.unmarshal(reader)
}
func (t *typeConstructor) Reader() TypeReader {
    return t.reader
}
func (t *typeConstructor) Type() reflect.Type {
    return t.typ
}

// Print render the type into a string. It is only used for testing.
func Print(v Type) string {
    buf := bytes.NewBufferString("")
    v.TypeName().Render(buf)
    return buf.String()
}

// NewInt8Type is a contructor for the representation of a uint64.
func NewInt8Type() Type {
    return &typeConstructor{
        signature:    "c",
        signatureIDL: "int8",
        typeName:     jen.Int8(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteInt8").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadInt8").Call(jen.Id(reader))
        },
        reader: constReader(1),
        typ:    reflect.TypeOf((int8)(0)),
    }
}

// NewUint8Type is a contructor for the representation of a uint64.
func NewUint8Type() Type {
    return &typeConstructor{
        signature:    "C",
        signatureIDL: "uint8",
        typeName:     jen.Uint8(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteUint8").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadUint8").Call(jen.Id(reader))
        },
        reader: constReader(1),
        typ:    reflect.TypeOf((uint8)(0)),
    }
}

// NewInt16Type is a contructor for the representation of a uint64.
func NewInt16Type() Type {
    return &typeConstructor{
        signature:    "w",
        signatureIDL: "int16",
        typeName:     jen.Int16(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteInt16").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadInt16").Call(jen.Id(reader))
        },
        reader: constReader(2),
        typ:    reflect.TypeOf((int16)(0)),
    }
}

// NewUint16Type is a contructor for the representation of a uint64.
func NewUint16Type() Type {
    return &typeConstructor{
        signature:    "W",
        signatureIDL: "uint16",
        typeName:     jen.Uint16(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteUint16").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadUint16").Call(jen.Id(reader))
        },
        reader: constReader(2),
        typ:    reflect.TypeOf((uint16)(0)),
    }
}

// NewIntType is a contructor for the representation of an int32.
func NewIntType() Type {
    return &typeConstructor{
        signature:    "i",
        signatureIDL: "int32",
        typeName:     jen.Int32(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteInt32").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadInt32").Call(jen.Id(reader))
        },
        reader: constReader(4),
        typ:    reflect.TypeOf((int32)(0)),
    }
}

// NewUIntType is a contructor for the representation of an uint32.
func NewUintType() Type {
    return &typeConstructor{
        signature:    "I",
        signatureIDL: "uint32",
        typeName:     jen.Uint32(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteUint32").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadUint32").Call(jen.Id(reader))
        },
        reader: constReader(4),
        typ:    reflect.TypeOf((uint32)(0)),
    }
}

// NewLongType is a contructor for the representation of a uint64.
func NewLongType() Type {
    return &typeConstructor{
        signature:    "l",
        signatureIDL: "int64",
        typeName:     jen.Int64(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteInt64").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadInt64").Call(jen.Id(reader))
        },
        reader: constReader(8),
        typ:    reflect.TypeOf((int64)(0)),
    }
}

// NewULongType is a contructor for the representation of a uint64.
func NewULongType() Type {
    return &typeConstructor{
        signature:    "L",
        signatureIDL: "uint64",
        typeName:     jen.Uint64(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteUint64").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadUint64").Call(jen.Id(reader))
        },
        reader: constReader(8),
        typ:    reflect.TypeOf((uint64)(0)),
    }
}

// NewFloatType is a contructor for the representation of a float32.
func NewFloatType() Type {
    return &typeConstructor{
        signature:    "f",
        signatureIDL: "float32",
        typeName:     jen.Float32(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteFloat32").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadFloat32").Call(jen.Id(reader))
        },
        reader: constReader(4),
        typ:    reflect.TypeOf((float32)(0)),
    }
}

// NewDoubleType is a contructor for the representation of a float32.
func NewDoubleType() Type {
    return &typeConstructor{
        signature:    "d",
        signatureIDL: "float64",
        typeName:     jen.Float64(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "WriteFloat64").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadFloat64").Call(jen.Id(reader))
        },
        reader: constReader(8),
        typ:    reflect.TypeOf((float64)(0)),
    }
}

// NewStringType is a contructor for the representation of a string.
func NewStringType() Type {
    return &typeConstructor{
        signature:    "s",
        signatureIDL: "str",
        typeName:     jen.String(),
        marshal: func(id string, writer string) *Statement {
            return jen.Id("basic.WriteString").Call(jen.Id(id),
                jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic",
                "ReadString").Call(jen.Id(reader))
        },
        reader: stringReader{},
        typ:    reflect.TypeOf(string("")),
    }
}

// NewVoidType is a contructor for the representation of the
// absence of a return type. Only used in the context of a returned
// type.
func NewVoidType() Type {
    return &typeConstructor{
        signature:    "v",
        signatureIDL: "nothing",
        typeName:     jen.Empty(),
        marshal: func(id string, writer string) *Statement {
            return jen.Nil()
        },
        unmarshal: func(reader string) *Statement {
            return jen.Empty()
        },
        reader: constReader(0),
        typ:    reflect.TypeOf(struct{}{}),
    }
}

// NewValueType is a contructor for the representation of a Value.
func NewValueType() Type {
    return &typeConstructor{
        signature:    "m",
        signatureIDL: "any",
        typeName:     jen.Qual("github.com/lugu/qiloop/type/value", "Value"),
        marshal: func(id string, writer string) *Statement {
            return jen.Id(id).Dot("Write").Call(jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/value", "NewValue").Call(jen.Id(reader))
        },
        reader: valueReader{},
        typ:    reflect.TypeOf((*interface{})(nil)),
    }
}

// NewBoolType is a contructor for the representation of a bool.
func NewBoolType() Type {
    return &typeConstructor{
        signature:    "b",
        signatureIDL: "bool",
        typeName:     jen.Bool(),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic", "WriteBool").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/basic", "ReadBool").Call(jen.Id(reader))
        },
        reader: constReader(1),
        typ:    reflect.TypeOf((bool)(true)),
    }
}

// NewMetaObjectType is a contructor for the representation of an
// object.
func NewMetaObjectType() Type {
    typ, _ := Parse(MetaObjectSignature)
    reader, err := MakeReader(MetaObjectSignature)
    if err != nil {
        panic(fmt.Errorf("invalid MetaObjectSignature: %s",
            MetaObjectSignature))
    }
    return &typeConstructor{
        signature:    MetaObjectSignature,
        signatureIDL: "MetaObject",
        typeName:     jen.Qual("github.com/lugu/qiloop/type/object", "MetaObject"),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/object", "WriteMetaObject").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/object", "ReadMetaObject").Call(jen.Id(reader))
        },
        reader: reader,
        typ:    typ.Type(),
    }
}

// NewObjectType is a contructor for the representation of a Value.
func NewObjectType() Type {
    sig := "(({I(Issss[(ss)<MetaMethodParameter,name,description>]s)<MetaMethod,uid,returnSignature,name,parametersSignature,description,parameters,returnDescription>}{I(Iss)<MetaSignal,uid,name,signature>}{I(Iss)<MetaProperty,uid,name,signature>}s)<MetaObject,methods,signals,properties,description>II)<ObjectReference,metaObject,serviceID,objectID>"
    typ, _ := Parse(sig)

    reader, _ := MakeReader(sig)
    return &typeConstructor{
        signature:    "o",
        signatureIDL: "obj",
        typeName:     jen.Qual("github.com/lugu/qiloop/type/object", "ObjectReference"),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/object", "WriteObjectReference").Call(jen.Id(id), jen.Id(writer))
        },
        unmarshal: func(reader string) *Statement {
            return jen.Qual("github.com/lugu/qiloop/type/object", "ReadObjectReference").Call(jen.Id(reader))
        },
        reader: reader,
        typ:    typ.Type(),
    }
}

func TypeIsObjectReference(t Type) bool {
    if t.Signature() != "o" {
        return false
    }
    name := t.TypeName()
    if reflect.DeepEqual(name,
        jen.Qual("github.com/lugu/qiloop/type/object",
            "ObjectReference"),
    ) {
        return true
    }
    return false
}

// NewUnknownType is a contructor for an unknown type.
func NewUnknownType() Type {
    return &typeConstructor{
        signature:    "X",
        signatureIDL: "unknown",
        typeName:     jen.Id("interface{}"),
        marshal: func(id string, writer string) *Statement {
            return jen.Qual("fmt", "Errorf").Call(
                jen.Lit("marshal unknown type: %v"),
                jen.Id(id),
            )
        },
        unmarshal: func(reader string) *Statement {
            return jen.List(jen.Nil(),
                jen.Qual("fmt", "Errorf").Call(
                    jen.Lit("unmarshal unknown type: %v"),
                    jen.Id(reader),
                ),
            )
        },
        reader: UnknownReader("X"),
        typ:    reflect.TypeOf((*error)(nil)),
    }
}

// NewListType is a contructor for the representation of a slice.
func NewListType(value Type) *ListType {
    return &ListType{value}
}

// ListType represents a slice.
type ListType struct {
    value Type
}

// Signature returns "[<signature>]" where <signature> is the
// signature of the type of the list.
func (l *ListType) Signature() string {
    return fmt.Sprintf("[%s]", l.value.Signature())
}

// SignatureIDL returns "Vec<signature>" where "signature" is the IDL
// signature of the type of the list.
func (l *ListType) SignatureIDL() string {
    return fmt.Sprintf("Vec<%s>", l.value.SignatureIDL())
}

// TypeName returns a statement to be inserted when the type is to be
// declared.
func (l *ListType) TypeName() *Statement {
    return jen.Index().Add(l.value.TypeName())
}

// RegisterTo adds the type to the TypeSet.
func (l *ListType) RegisterTo(s *TypeSet) {
    l.value.RegisterTo(s)
    return
}

// TypeDeclaration writes the type declaration into file.
func (l *ListType) TypeDeclaration(file *jen.File) {
    return
}

// Marshal returns a statement which represent the code needed to put
// the variable "id" into the io.Writer "writer" while returning an
// error.
func (l *ListType) Marshal(listID string, writer string) *Statement {
    return jen.Func().Params().Params(jen.Error()).Block(
        jen.Err().Op(":=").Qual("github.com/lugu/qiloop/type/basic", "WriteUint32").Call(jen.Id("uint32").Call(
            jen.Id("len").Call(jen.Id(listID))),
            jen.Id(writer)),
        jen.Id(`if (err != nil) {
            return fmt.Errorf("write slice size: %s", err)
        }`),
        jen.For(
            jen.Id("_, v := range "+listID),
        ).Block(
            jen.Err().Op("=").Add(l.value.Marshal("v", writer)),
            jen.Id(`if (err != nil) {
                return fmt.Errorf("write slice value: %s", err)
            }`),
        ),
        jen.Return(jen.Nil()),
    ).Call()
}

// Unmarshal returns a statement which represent the code needed to read
// from a reader "reader" of type io.Reader and returns both the value
// read and an error.
func (l *ListType) Unmarshal(reader string) *Statement {
    return jen.Func().Params().Params(
        jen.Id("b").Index().Add(l.value.TypeName()),
        jen.Err().Error(),
    ).Block(
        jen.Id("size, err := basic.ReadUint32").Call(jen.Id(reader)),
        jen.If(jen.Id("err != nil")).Block(
            jen.Return(jen.Id("b"), jen.Qual("fmt", "Errorf").Call(jen.Id(`"read slice size: %s", err`)))),
        jen.Id("b").Op("=").Id("make").Call(l.TypeName(), jen.Id("size")),
        jen.For(
            jen.Id("i := 0; i < int(size); i++"),
        ).Block(
            jen.Id("b[i], err =").Add(l.value.Unmarshal(reader)),
            jen.Id(`if (err != nil) {
                return b, fmt.Errorf("read slice value: %s", err)
            }`),
        ),
        jen.Return(jen.Id("b"), jen.Nil()),
    ).Call()
}

// Reader returns a list TypeReader.
func (l *ListType) Reader() TypeReader {
    return varReader{
        reader: l.value.Reader(),
    }
}

// Type returns the Go representation of the type.
func (l *ListType) Type() reflect.Type {
    return reflect.SliceOf(l.value.Type())
}

// NewMapType is a contructor for the representation of a map.
func NewMapType(key, value Type) *MapType {
    return &MapType{key, value}
}

// MapType represents a map.
type MapType struct {
    key   Type
    value Type
}

// Signature returns "{<signature key><signature value>}" where
// <signature key> is the signature of the key and <signature value>
// the signature of the value.
func (m *MapType) Signature() string {
    return fmt.Sprintf("{%s%s}", m.key.Signature(), m.value.Signature())
}

// SignatureIDL returns "map<key><value>" where "key" is the IDL
// signature of the key and "value" the IDL signature of the value.
func (m *MapType) SignatureIDL() string {
    return fmt.Sprintf("Map<%s,%s>", m.key.SignatureIDL(), m.value.SignatureIDL())
}

// TypeName returns a statement to be inserted when the type is to be
// declared.
func (m *MapType) TypeName() *Statement {
    return jen.Map(m.key.TypeName()).Add(m.value.TypeName())
}

// RegisterTo adds the type to the TypeSet.
func (m *MapType) RegisterTo(s *TypeSet) {
    m.key.RegisterTo(s)
    m.value.RegisterTo(s)
    return
}

// TypeDeclaration writes the type declaration into file.
func (m *MapType) TypeDeclaration(file *jen.File) {
    return
}

// Reader returns a map TypeReader.
func (m *MapType) Reader() TypeReader {
    return varReader{
        reader: tupleReader([]memberReader{
            memberReader{
                name:   "key",
                reader: m.key.Reader(),
            },
            memberReader{
                name:   "value",
                reader: m.value.Reader(),
            },
        }),
    }
}

// Marshal returns a statement which represent the code needed to put
// the variable "id" into the io.Writer "writer" while returning an
// error.
func (m *MapType) Marshal(mapID string, writer string) *Statement {
    return jen.Func().Params().Params(jen.Error()).Block(
        jen.Err().Op(":=").Qual("github.com/lugu/qiloop/type/basic", "WriteUint32").Call(jen.Id("uint32").Call(
            jen.Id("len").Call(jen.Id(mapID))),
            jen.Id(writer)),
        jen.Id(`if (err != nil) {
            return fmt.Errorf("write map size: %s", err)
        }`),
        jen.For(
            jen.Id("k, v := range "+mapID),
        ).Block(
            jen.Err().Op("=").Add(m.key.Marshal("k", writer)),
            jen.Id(`if (err != nil) {
                return fmt.Errorf("write map key: %s", err)
            }`),
            jen.Err().Op("=").Add(m.value.Marshal("v", writer)),
            jen.Id(`if (err != nil) {
                return fmt.Errorf("write map value: %s", err)
            }`),
        ),
        jen.Return(jen.Nil()),
    ).Call()
}

// Unmarshal returns a statement which represent the code needed to read
// from a reader "reader" of type io.Reader and returns both the value
// read and an error.
func (m *MapType) Unmarshal(reader string) *Statement {
    return jen.Func().Params().Params(
        jen.Id("m").Map(m.key.TypeName()).Add(m.value.TypeName()),
        jen.Err().Error(),
    ).Block(
        jen.Id("size, err := basic.ReadUint32").Call(jen.Id(reader)),
        jen.If(jen.Id("err != nil")).Block(
            jen.Return(jen.Id("m"), jen.Qual("fmt", "Errorf").Call(jen.Id(`"read map size: %s", err`)))),
        jen.Id("m").Op("=").Id("make").Call(m.TypeName(), jen.Id("size")),
        jen.For(
            jen.Id("i := 0; i < int(size); i++"),
        ).Block(
            jen.Id("k, err :=").Add(m.key.Unmarshal(reader)),
            jen.Id(`if (err != nil) {
                return m, fmt.Errorf("read map key (%d/%d): %s", i+1, size, err)
            }`),
            jen.Id("v, err :=").Add(m.value.Unmarshal(reader)),
            jen.Id(`if (err != nil) {
                return m, fmt.Errorf("read map value (%d/%d): %s", i+1, size, err)
            }`),
            jen.Id("m[k] = v"),
        ),
        jen.Return(jen.Id("m"), jen.Nil()),
    ).Call()
}

// Type returns the Go representation of the type.
func (m *MapType) Type() reflect.Type {
    return reflect.MapOf(m.key.Type(), m.value.Type())
}

// NewMemberType is a contructor for the representation of a field in
// a struct.
func NewMemberType(name string, value Type) MemberType {
    return MemberType{name, value}
}

// MemberType a field in a struct.
type MemberType struct {
    Name string
    Type Type
}

// Title is the public name of the field.
func (m MemberType) Title() string {
    return CleanName(m.Name)
}

// NewTupleType is a contructor for the representation of a series of
// types. Used to describe a method parameters list.
func NewTupleType(values []Type) *TupleType {
    var tuple TupleType
    tuple.Members = make([]MemberType, 0)
    for i, v := range values {
        tuple.Members = append(tuple.Members,
            MemberType{fmt.Sprintf("P%d", i), v})
    }
    return &tuple
}

// TupleType a list of a parameter of a method.
type TupleType struct {
    Members []MemberType
}

// Signature returns "(<signature 1><signature 2>...)" where
// <signature X> is the signature of the elements.
func (t *TupleType) Signature() string {
    sig := "("
    for _, m := range t.Members {
        sig += m.Type.Signature()
    }
    sig += ")"
    return sig
}

// ParamIDL returns "name1 signature1, name2 signature2" where
// signatureX is the signature of the elements.
func (t *TupleType) ParamIDL() string {
    var sig string
    for i, typ := range t.Members {
        sig += typ.Name + ": " + typ.Type.SignatureIDL()
        if i != len(t.Members)-1 {
            sig += ", "
        }
    }
    return sig
}

// SignatureIDL returns "Tuple<signature1,signature2>" where
// signatureX is the signature of the elements.
func (t *TupleType) SignatureIDL() string {
    var sig string
    for i, typ := range t.Members {
        sig += typ.Type.SignatureIDL()
        if i != len(t.Members)-1 {
            sig += ","
        }
    }
    return fmt.Sprintf("Tuple<%s>", sig)
}

// Params returns a statement representing the list of parameter of
// a method.
func (t *TupleType) Params() *Statement {
    arguments := make([]jen.Code, len(t.Members))
    for i, m := range t.Members {
        arguments[i] = jen.Id(m.Name).Add(m.Type.TypeName())
    }
    return jen.Params(arguments...)
}

// TypeName returns a statement to be inserted when the type is to be
// declared.
func (t *TupleType) TypeName() *Statement {
    params := make([]jen.Code, 0)
    for _, typ := range t.Members {
        params = append(params, jen.Id(strings.Title(typ.Name)).Add(typ.Type.TypeName()))
    }
    return jen.Struct(params...)
}

// RegisterTo adds the type to the TypeSet.
func (t *TupleType) RegisterTo(s *TypeSet) {
    for _, m := range t.Members {
        m.Type.RegisterTo(s)
    }
    return
}

// TypeDeclaration writes the type declaration into file.
func (t *TupleType) TypeDeclaration(*jen.File) {
    return
}

// Marshal returns a statement which represent the code needed to put
// the variable "id" into the io.Writer "writer" while returning an
// error.
func (t *TupleType) Marshal(tupleID string, writer string) *Statement {
    statements := make([]jen.Code, 0)
    for _, typ := range t.Members {
        s1 := jen.Err().Op("=").Add(typ.Type.Marshal(tupleID+"."+strings.Title(typ.Name), writer))
        s2 := jen.Id(`if (err != nil) {
            return fmt.Errorf("write tuple member: %s", err)
        }`)
        statements = append(statements, s1)
        statements = append(statements, s2)
    }
    statements = append(statements, jen.Return(jen.Nil()))
    return jen.Func().Params().Params(jen.Error()).Block(
        statements...,
    ).Call()
}

// Unmarshal returns a statement which represent the code needed to read
// from a reader "reader" of type io.Reader and returns both the value
// read and an error.
func (t *TupleType) Unmarshal(reader string) *Statement {
    statements := make([]jen.Code, 0)
    for _, typ := range t.Members {
        s1 := jen.List(jen.Id("s."+strings.Title(typ.Name)), jen.Err()).Op("=").Add(typ.Type.Unmarshal(reader))
        s2 := jen.Id(`if (err != nil) {
            return s, fmt.Errorf("read tuple member: %s", err)
        }`)
        statements = append(statements, s1)
        statements = append(statements, s2)
    }
    statements = append(statements, jen.Return(jen.Id("s"), jen.Nil()))
    return jen.Func().Params().Params(
        jen.Id("s").Add(t.TypeName()),
        jen.Err().Error(),
    ).Block(
        statements...,
    ).Call()
}

// Reader returns a map TypeReader.
func (t *TupleType) Reader() TypeReader {
    readers := make([]memberReader, len(t.Members))
    for i, m := range t.Members {
        readers[i] = memberReader{
            name:   m.Name,
            reader: m.Type.Reader(),
        }
    }
    return tupleReader(readers)
}

// Type returns the Go representation of the type.
func (t *TupleType) Type() reflect.Type {
    fields := make([]reflect.StructField, len(t.Members))
    var offset uintptr = 0
    for i, m := range t.Members {
        typ := m.Type.Type()
        fields[i] = reflect.StructField{
            Name:      CleanName(m.Name),
            PkgPath:   typ.PkgPath(),
            Type:      typ,
            Index:     []int{i},
            Offset:    offset,
            Anonymous: false,
        }
        offset += typ.Size()
    }
    return reflect.StructOf(fields)
}

// ConvertMetaObjects replace any element type which has the same
// signature as MetaObject with an element of the type
// object.MetaObject. This is required to generate proxy services
// which implements the object.Object interface and avoid a circular
// dependency.
func (t *TupleType) ConvertMetaObjects() {
    for i, m := range t.Members {
        if m.Type.Signature() == MetaObjectSignature {
            t.Members[i].Type = NewMetaObjectType()
        }
    }
}

// NewStructType is a contructor for the representation of a struct.
func NewStructType(name string, members []MemberType) *StructType {
    return &StructType{name, members}
}

// StructType represents a struct.
type StructType struct {
    Name    string
    Members []MemberType
}

// Signature returns the signature of the struct.
func (s *StructType) Signature() string {
    if len(s.Members) == 0 {
        return fmt.Sprintf("()<%s>", s.Name)
    }
    types := ""
    names := make([]string, 0, len(s.Members))
    for _, v := range s.Members {
        names = append(names, v.Name)
        types += v.Type.Signature()
    }
    return fmt.Sprintf("(%s)<%s,%s>", types,
        s.Name, strings.Join(names, ","))
}

func (s *StructType) name() string {
    return CleanName(s.Name)
}

// SignatureIDL returns the idl signature of the struct.
func (s *StructType) SignatureIDL() string {
    return s.Name
}

// TypeName returns a statement to be inserted when the type is to be
// declared.
func (s *StructType) TypeName() *Statement {
    return jen.Id(s.name())
}

// RegisterTo adds the type to the TypeSet.
// Structure name is updated if needed  after collision
// resolution. Register search if a type is in the TypeSet under a
// given name. If the same name and signature is already present it
// does nothing. otherwise it adds the type and search for a new which
// does not conflict with the names already present.
func (s *StructType) RegisterTo(set *TypeSet) {
    for _, v := range s.Members {
        v.Type.RegisterTo(set)
    }
    s.Name = set.ResolveCollision(s.Name, s.Signature())
    if set.Search(s.Name) == nil {
        set.Types = append(set.Types, s)
        set.Names = append(set.Names, s.Name)
    }
}

// TypeDeclaration writes the type declaration into file.
func (s *StructType) TypeDeclaration(file *jen.File) {
    fields := make([]jen.Code, len(s.Members))
    for i, v := range s.Members {
        fields[i] = jen.Id(v.Title()).Add(v.Type.TypeName())
    }
    file.Commentf("%s is serializable", s.name())
    file.Type().Id(s.name()).Struct(fields...)

    readFields := make([]jen.Code, len(s.Members)+1)
    writeFields := make([]jen.Code, len(s.Members)+1)
    for i, v := range s.Members {
        readFields[i] = jen.If(
            jen.Id("s."+v.Title()+", err =").Add(v.Type.Unmarshal("r")),
            jen.Id("err != nil")).Block(
            jen.Return(jen.Id("s"),
                jen.Qual("fmt", "Errorf").Call(
                    jen.Lit(`read `+v.Title()+` field: %s`),
                    jen.Id("err"),
                )),
        )
        writeFields[i] = jen.If(
            jen.Id("err :=").Add(v.Type.Marshal("s."+v.Title(), "w")),
            jen.Err().Op("!=").Nil(),
        ).Block(
            jen.Return(jen.Qual("fmt", "Errorf").Call(
                jen.Lit(`write `+v.Title()+` field: %s`),
                jen.Id("err"),
            )),
        )
    }
    readFields[len(s.Members)] = jen.Return(jen.Id("s"), jen.Nil())
    writeFields[len(s.Members)] = jen.Return(jen.Nil())

    file.Commentf("read%s unmarshalls %s", s.name(), s.name())
    file.Func().Id("read"+s.name()).Params(
        jen.Id("r").Id("io.Reader"),
    ).Params(
        jen.Id("s").Id(s.name()), jen.Err().Error(),
    ).Block(readFields...)
    file.Commentf("write%s marshalls %s", s.name(), s.name())
    file.Func().Id("write"+s.name()).Params(
        jen.Id("s").Id(s.name()),
        jen.Id("w").Qual("io", "Writer"),
    ).Params(jen.Err().Error()).Block(writeFields...)
}

// Marshal returns a statement which represent the code needed to put
// the variable "id" into the io.Writer "writer" while returning an
// error.
func (s *StructType) Marshal(structID string, writer string) *Statement {
    return jen.Id("write"+s.name()).Call(jen.Id(structID), jen.Id(writer))
}

// Unmarshal returns a statement which represent the code needed to read
// from a reader "reader" of type io.Reader and returns both the value
// read and an error.
func (s *StructType) Unmarshal(reader string) *Statement {
    return jen.Id("read" + s.name()).Call(jen.Id(reader))
}

// Reader returns a struct TypeReader.
func (s *StructType) Reader() TypeReader {
    readers := make([]memberReader, len(s.Members))
    for i, m := range s.Members {
        readers[i] = memberReader{
            name:   m.Name,
            reader: m.Type.Reader(),
        }
    }
    return tupleReader(readers)
}

// Type returns the Go representation of the type.
func (s *StructType) Type() reflect.Type {
    fields := make([]reflect.StructField, len(s.Members))
    var offset uintptr = 0
    for i, m := range s.Members {
        typ := m.Type.Type()
        fields[i] = reflect.StructField{
            Name:      CleanName(m.Name),
            PkgPath:   typ.PkgPath(),
            Type:      typ,
            Index:     []int{i},
            Offset:    offset,
            Anonymous: false,
        }
        offset += typ.Size()
    }
    return reflect.StructOf(fields)
}

// EnumType represents a const.
type EnumType struct {
    Name   string
    Values map[string]int
}

// EnumMember is used during Enum parsing
type EnumMember struct {
    Const string
    Value int
}

// NewEnumType returns an enum type
func NewEnumType(name string, values map[string]int) Type {
    return &EnumType{
        Name:   name,
        Values: values,
    }
}

// Signature returns "i" since enum are integer constant.
func (e *EnumType) Signature() string {
    return "i"
}

// SignatureIDL returns the name of the enum.
func (e *EnumType) SignatureIDL() string {
    return e.Name
}

// TypeName returns a statement to be inserted when the type is to be
// declared.
func (e *EnumType) TypeName() *Statement {
    return jen.Id(e.Name)
}

// RegisterTo adds the enum to the type set.
func (e *EnumType) RegisterTo(set *TypeSet) {
    // do not register anonymous enum
    if e.Name == "" {
        return
    }
    for i, name := range set.Names {
        if name == e.Name {
            if set.Types[i].Signature() != e.Signature() {
                log.Printf("type set collision %s: %s vs %s",
                    name, set.Types[i].Signature(),
                    e.Signature())
            }
            return
        }
    }
    set.Names = append(set.Names, e.Name)
    set.Types = append(set.Types, e)
}

// TypeDeclaration writes the type declaration into file.
func (e *EnumType) TypeDeclaration(file *jen.File) {
    file.Type().Id(e.Name).Int()
    var defs = make([]jen.Code, 0)
    for i, v := range e.Values {
        defs = append(defs, jen.Id(CleanName(i)).Op("=").Lit(v))
    }
    file.Const().Defs(defs...)
}

// Marshal returns a statement which represent the code needed to put
// the variable "id" into the io.Writer "writer" while returning an
// error.
func (e *EnumType) Marshal(id string, writer string) *Statement {
    return NewIntType().Marshal(id, writer)
}

// Unmarshal returns a statement which represent the code needed to read
// from a reader "reader" of type io.Reader and returns both the value
// read and an error.
func (e *EnumType) Unmarshal(reader string) *Statement {
    return NewIntType().Unmarshal(reader)
}

// Reader returns an enum TypeReader.
func (e *EnumType) Reader() TypeReader {
    return constReader(4)
}

// Type returns the Go representation of the type.
func (e *EnumType) Type() reflect.Type {
    return reflect.TypeOf((int)(0))
}