Read and write of structs, lists, maps, and sets


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@666375 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/alterl/src/thrift_protocol.erl b/lib/alterl/src/thrift_protocol.erl
index 66e19b3..75c2d4b 100644
--- a/lib/alterl/src/thrift_protocol.erl
+++ b/lib/alterl/src/thrift_protocol.erl
@@ -27,10 +27,6 @@
                    data = Data}}.
 
 
-write(#protocol{module = Module,
-                data = ModuleData}, Data) ->
-    Module:write(ModuleData, Data).
-
 typeid_to_atom(?tType_STOP) -> field_stop;
 typeid_to_atom(?tType_VOID) -> void;
 typeid_to_atom(?tType_BOOL) -> bool;
@@ -45,10 +41,92 @@
 typeid_to_atom(?tType_SET) -> set;
 typeid_to_atom(?tType_LIST) -> list.
     
+
+term_to_typeid(void) -> ?tType_VOID;
+term_to_typeid(bool) -> ?tType_BOOL;
+term_to_typeid(byte) -> ?tType_BYTE;
+term_to_typeid(double) -> ?tType_DOUBLE;
+term_to_typeid(i16) -> ?tType_I16;
+term_to_typeid(i32) -> ?tType_I32;
+term_to_typeid(i64) -> ?tType_I64;
+term_to_typeid(string) -> ?tType_STRING;
+term_to_typeid({struct, _}) -> ?tType_STRUCT;
+term_to_typeid({map, _, _}) -> ?tType_MAP;
+term_to_typeid({set, _}) -> ?tType_SET;
+term_to_typeid({list, _}) -> ?tType_LIST.
+
+
+%% Structure is like:
+%%    [{Fid, Type}, ...]
+read(IProto, {struct, Structure}) when is_list(Structure) ->
+    SWithIndices = [{Fid, {Type, Index}} ||
+                       {{Fid, Type}, Index} <-
+                           lists:zip(Structure, lists:seq(1, length(Structure)))],
+    % Fid -> {Type, Index}
+    SDict = dict:from_list(SWithIndices),
+
+
+    ok = read(IProto, struct_begin),
+    RDict = read_struct_loop(IProto, SDict, dict:new()),
+
+    List = [case dict:find(Index, RDict) of
+                {ok, Val} -> Val;
+                error     -> undefined
+            end || Index <- lists:seq(1, length(Structure))],
+    {ok, list_to_tuple(List)};
+
+read(IProto, {list, Type}) ->
+    #protocol_list_begin{etype = EType, size = Size} =
+        read(IProto, list_begin),
+    List = [Result || {ok, Result} <- 
+                          [read(IProto, Type) || _X <- lists:seq(1, Size)]],
+    ok = read(IProto, list_end),
+    {ok, List};
+
+read(IProto, {map, KeyType, ValType}) ->
+    #protocol_map_begin{size = Size} =
+        read(IProto, map_begin),
+
+    List = [{Key, Val} || {{ok, Key}, {ok, Val}} <- 
+                              [{read(IProto, KeyType),
+                                read(IProto, ValType)} || _X <- lists:seq(1, Size)]],
+    ok = read(IProto, map_end),
+    {ok, dict:from_list(List)};
+
+read(IProto, {set, Type}) ->
+    #protocol_set_begin{etype = _EType,
+                        size = Size} =
+        read(IProto, set_begin),
+    List = [Result || {ok, Result} <- 
+                          [read(IProto, Type) || _X <- lists:seq(1, Size)]],
+    ok = read(IProto, set_end),
+    {ok, sets:from_list(List)};
+
 read(#protocol{module = Module,
                data = ModuleData}, ProtocolType) ->
     Module:read(ModuleData, ProtocolType).
 
+read_struct_loop(IProto, SDict, RDict) ->
+    #protocol_field_begin{type = FType, id = Fid, name = Name} =
+        thrift_protocol:read(IProto, field_begin),
+    case {FType, Fid} of
+        {?tType_STOP, _} ->
+            RDict;
+        _Else ->
+            case dict:find(Fid, SDict) of
+                {ok, {Type, Index}} ->
+                    {ok, Val} = read(IProto, Type),
+                    thrift_protocol:read(IProto, field_end),
+                    NewRDict = dict:store(Index, Val, RDict),
+                    read_struct_loop(IProto, SDict, NewRDict);
+                _Else2 ->
+                    error_logger:info_msg("Skipping fid ~p~n", [Fid]),
+                    FTypeAtom = thrift_protocol:typeid_to_atom(FType),
+                    thrift_protocol:skip(IProto, FTypeAtom),
+                    read(IProto, field_end),
+                    read_struct_loop(IProto, SDict, RDict)
+            end
+    end.
 
 
 skip(Proto, struct) ->
@@ -118,3 +196,98 @@
                            Map#protocol_list_begin{size = Size - 1});
         0 -> ok
     end.
+
+
+%%--------------------------------------------------------------------
+%% Function: write(OProto, {Type, Data}) -> ok
+%% 
+%% Type = {struct, StructDef} |
+%%        {list, Type} |
+%%        {map, KeyType, ValType} |
+%%        {set, Type} |
+%%        BaseType
+%%
+%% Data =
+%%         tuple()  -- for struct
+%%       | list()   -- for list
+%%       | dictionary()   -- for map
+%%       | set()    -- for set
+%%       | term()   -- for base types
+%%
+%% Description: 
+%%--------------------------------------------------------------------
+write(Proto, {{struct, StructDef}, Data})
+  when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) ->
+    ok = write(Proto, #protocol_struct_begin{}),
+    ok = struct_write_loop(Proto, StructDef, tuple_to_list(Data)),
+    ok = write(Proto, struct_end),
+    ok;
+
+write(Proto, {{list, Type}, Data})
+  when is_list(Data) ->
+    ok = write(Proto,
+               #protocol_list_begin{
+                 etype = term_to_typeid(Type),
+                 size = length(Data)
+                }),
+    lists:foreach(fun(Elem) ->
+                          ok = write(Proto, {Type, Elem})
+                  end,
+                  Data),
+    ok = write(Proto, list_end),
+    ok;
+
+write(Proto, {{map, KeyType, ValType}, Data}) ->
+    DataList = dict:to_list(Data),
+    ok = write(Proto,
+               #protocol_map_begin{
+                 ktype = term_to_typeid(KeyType),
+                 vtype = term_to_typeid(ValType),
+                 size = length(DataList)
+                 }),
+    lists:foreach(fun({KeyData, ValData}) ->
+                          ok = write(Proto, {KeyType, KeyData}),
+                          ok = write(Proto, {ValType, ValData})
+                  end,
+                  DataList),
+    ok = write(Proto, map_end),
+    ok;
+
+write(Proto, {{set, Type}, Data}) ->
+    true = sets:is_set(Data),
+    DataList = sets:to_list(Data),
+    ok = write(Proto,
+               #protocol_set_begin{
+                 etype = term_to_typeid(Type),
+                 size = length(DataList)
+                 }),
+    lists:foreach(fun(Elem) ->
+                          ok = write(Proto, {Type, Elem})
+                  end,
+                  DataList),
+    ok = write(Proto, set_end),
+    ok;
+
+write(#protocol{module = Module,
+                data = ModuleData}, Data) ->
+    Module:write(ModuleData, Data).
+
+
+struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
+    case Data of
+        undefined ->
+            % null fields are skipped in response
+            skip;
+        _ ->
+            ok = write(Proto,
+                       #protocol_field_begin{
+                         type = term_to_typeid(Type),
+                         id = Fid
+                        }),
+            ok = write(Proto, {Type, Data}),
+            ok = write(Proto, field_end)
+    end,
+    struct_write_loop(Proto, RestStructDef, RestData);
+struct_write_loop(Proto, [], []) ->
+    ok = write(Proto, field_stop),
+    ok.