[thrift] preliminary Erlang support (initial import)

Summary:
 * missing {list,map,set}s, inheritance is spotty
 * loose source code, plus everything is one process (application / gen_server behavior is forthcoming)
 * codegen is a mess, need t_fp_generator

Test Plan:
 * codegen invoked without -erl generates identical code for test/
 * calculatorHandler plus 'thrift -erl -r tutorial.thrift' more or less works

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665146 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/erl/src-loose/transport/tBufferedTransport.erl b/lib/erl/src-loose/transport/tBufferedTransport.erl
new file mode 100644
index 0000000..1cc809d
--- /dev/null
+++ b/lib/erl/src-loose/transport/tBufferedTransport.erl
@@ -0,0 +1,34 @@
+-module(tBufferedTransport).
+
+-include("thrift/thrift.hrl").
+-include("thrift/transport/tBufferedTransport.hrl").
+
+-export([new/1, isOpen/1, open/1, close/1, read/2, write_MUTABLE/2, flush_MUTABLE/1]).
+
+new(Transport) ->
+    #tBufferedTransport{transport=Transport, wbuf=""}.
+
+transport(This) -> % local accessor
+    This#tBufferedTransport.transport.
+
+isOpen(This) ->
+    ?M0(transport(This), isOpen).
+
+open(This) ->
+    ?M0(transport(This), open).
+
+close(This) ->
+    ?M0(transport(This), close).
+
+read(This, Sz) ->
+    ?M1(transport(This), read, Sz).
+
+write_MUTABLE(This, Buf) -> % be sure to rebind This to the retval
+    Wbuf = This#tBufferedTransport.wbuf,
+    This#tBufferedTransport{wbuf=Wbuf++Buf}. % TODO: ++ efficiency?
+
+flush_MUTABLE(This) -> % be sure to rebind This to the retval
+    Wbuf = This#tBufferedTransport.wbuf,
+    ?M1(transport(This), write, Wbuf),
+    ?M0(transport(This), flush),
+    This#tBufferedTransport{wbuf=""}. % TODO: ++ efficiency?
diff --git a/lib/erl/src-loose/transport/tBufferedTransport.hrl b/lib/erl/src-loose/transport/tBufferedTransport.hrl
new file mode 100644
index 0000000..d8d71e1
--- /dev/null
+++ b/lib/erl/src-loose/transport/tBufferedTransport.hrl
@@ -0,0 +1 @@
+-record(tBufferedTransport, {transport, wbuf}).
diff --git a/lib/erl/src-loose/transport/tServerSocket.erl b/lib/erl/src-loose/transport/tServerSocket.erl
new file mode 100644
index 0000000..239af6e
--- /dev/null
+++ b/lib/erl/src-loose/transport/tServerSocket.erl
@@ -0,0 +1,44 @@
+-module(tServerSocket).
+-include("tServerSocket.hrl").
+
+-export([new/1, listen_MUTABLE/1, accept_MUTABLE/1, close/1]).
+
+new(Port) ->
+    #tServerSocket{port=Port, handle=nil}.
+
+listen_MUTABLE(This) ->
+    Port = This#tServerSocket.port,
+    Options = [binary, {packet, 0}, {active, false}], % was []
+
+    case gen_tcp:listen(Port, Options) of 
+	{ok, ListenSocket} ->
+	    This#tServerSocket{handle=ListenSocket}
+	% {error, _} -> 
+	% TODO: no error handling in Ruby version?
+    end.
+
+accept_MUTABLE(This) ->
+    if 
+	This#tServerSocket.handle /= nil ->
+	    case gen_tcp:accept(This#tServerSocket.handle) of
+		{ok, Socket} ->
+		    tSocket:setHandle_MUTABLE( tSocket:new(), Socket )
+	        % {error, _} -> 
+                % TODO: no error handling in Ruby version?
+	    end;
+	true ->
+	    nil
+    end.
+
+close(This) ->
+    if 
+ 	This#tServerSocket.handle /= nil ->
+	    case gen_tcp:close(This#tServerSocket.handle) of 
+		ok ->
+		    ok
+	        % {error, _} -> 
+                % TODO: no error handling in Ruby version?
+	    end;
+	true ->
+	    ok
+    end.
diff --git a/lib/erl/src-loose/transport/tServerSocket.hrl b/lib/erl/src-loose/transport/tServerSocket.hrl
new file mode 100644
index 0000000..34ed320
--- /dev/null
+++ b/lib/erl/src-loose/transport/tServerSocket.hrl
@@ -0,0 +1 @@
+-record(tServerSocket, {port, handle}).
diff --git a/lib/erl/src-loose/transport/tSocket.erl b/lib/erl/src-loose/transport/tSocket.erl
new file mode 100644
index 0000000..850c3b9
--- /dev/null
+++ b/lib/erl/src-loose/transport/tSocket.erl
@@ -0,0 +1,96 @@
+-module(tSocket).
+
+-include("thrift/thrift.hrl").
+-include("thrift/transport/tTransportException.hrl").
+% -include("thrift/transport/tTransport.hrl").
+-include("thrift/transport/tSocket.hrl").
+
+-export([new/0, new/1, new/2, setHandle_MUTABLE/2, open_MUTABLE/1, isOpen/1, write/2, read/2, close_MUTABLE/1, readAll/2]).
+
+new(Host, Port) ->
+    #tSocket{host=Host, port=Port, handle=nil}. % WATCH
+
+new()     -> new("localhost", 9090).
+new(Host) -> new(Host, 9090).
+    
+setHandle_MUTABLE(This, Handle) ->
+    This#tSocket{handle=Handle}.
+
+open_MUTABLE(This) -> 
+    Host = This#tSocket.host,
+    Port = This#tSocket.port,
+    Options = [],
+
+    case gen_tcp:connect(Host, Port, Options) of
+	{error, _} ->
+	    throw(tTransportException:new(
+		    ?tTransportException_NOT_OPEN,
+		    "Could not connect to " ++ Host ++ ":" ++ Port)
+		 ),
+	    {error, This}; % cpiro not reached?
+	{ok, Socket} ->
+	    {ok, This#tSocket{handle=Socket}}
+    end.
+
+handle(This) ->
+    This#tSocket.handle.
+
+isOpen(This) ->
+    handle(This) /= nil.
+
+write(This, Str) ->
+    Val = gen_tcp:send(handle(This), Str),
+
+    %% io:format("WRITE |~p|(~p)~n", [Str,Val]),
+    
+    case Val of
+	{error, _} ->
+	    throw(tTransportException:new(?tTransportException_NOT_OPEN, "in write"));
+	ok ->
+	    ok
+    end.
+
+read(This, Sz) ->
+    case gen_tcp:recv(handle(This), Sz) of
+	{ok, []} ->
+	    { Host, Port } = { This#tSocket.host, This#tSocket.port },
+	    throw(tTransportException:new(?tTransportException_UNKNOWN, "TSocket: Could not read " ++ Sz ++ "bytes from " ++ Host ++ ":" ++ Port));
+	{ok, Data} ->
+	    Data;
+	{error, Error} ->
+	    io:format("in tSocket:read/2: gen_tcp:recv(~p, ~p) => {error, ~p}~n",
+		      [handle(This), Sz, Error]),
+	    throw(tTransportException:new(?tTransportException_NOT_OPEN, "in tSocket:read/2: gen_tcp:recv"))
+	end.
+	    
+close_MUTABLE(This) ->
+    if
+	This#tSocket.handle == nil ->
+	    This;
+	true ->
+	    gen_tcp:close(handle(This)),
+	    This#tSocket{handle=nil}
+    end.
+
+readAll(This, Sz) ->
+    readAll_loop(This, Sz, "", 0).
+
+readAll_loop(This, Sz, Buff, Have) ->
+    if 
+	Have < Sz ->
+	    Chunk = ?M1(This, read, Sz - Have),
+
+	    %% man gen_tcp:
+	    %% exactly Length bytes are returned, or an error; 
+	    %% possibly discarding less than Length bytes of data when 
+	    %% the socket gets closed from the other side.
+
+	    %% io:format("READ |~p|~n", [Chunk]),
+
+	    Have1 = Have + (Sz-Have), % length(Chunk)
+	    Buff1 = Buff ++ Chunk, % TODO: ++ efficiency?
+	    readAll_loop(This, Sz, Buff1, Have1);
+	true ->
+	    Buff
+    end.
+
diff --git a/lib/erl/src-loose/transport/tSocket.hrl b/lib/erl/src-loose/transport/tSocket.hrl
new file mode 100644
index 0000000..dc1cc20
--- /dev/null
+++ b/lib/erl/src-loose/transport/tSocket.hrl
@@ -0,0 +1 @@
+-record(tSocket, {host, port, handle}).
diff --git a/lib/erl/src-loose/transport/tTransport.erl b/lib/erl/src-loose/transport/tTransport.erl
new file mode 100644
index 0000000..91b7228
--- /dev/null
+++ b/lib/erl/src-loose/transport/tTransport.erl
@@ -0,0 +1,4 @@
+-module(tTransport).
+
+-include("thrift/transport/tTransportException.hrl").
+
diff --git a/lib/erl/src-loose/transport/tTransportException.erl b/lib/erl/src-loose/transport/tTransportException.erl
new file mode 100644
index 0000000..b31bb20
--- /dev/null
+++ b/lib/erl/src-loose/transport/tTransportException.erl
@@ -0,0 +1,15 @@
+-module(tTransportException).
+
+-include("thrift/thrift.hrl").
+-include("thrift/transport/tTransportException.hrl").
+
+-export([new/0, new/1, new/2, message/1]).
+
+new(Type, Message) ->
+    #tTransportException{type = Type, message = Message}.
+
+new()     -> new(?tTransportException_UNKNOWN, nil). % WATCH
+new(Type) -> new(Type, nil). % WATCH
+
+message(This) ->
+    ?ATTR(message).
diff --git a/lib/erl/src-loose/transport/tTransportException.hrl b/lib/erl/src-loose/transport/tTransportException.hrl
new file mode 100644
index 0000000..fa8554b
--- /dev/null
+++ b/lib/erl/src-loose/transport/tTransportException.hrl
@@ -0,0 +1,7 @@
+-define(tTransportException_UNKNOWN, 0).
+-define(tTransportException_NOT_OPEN, 1).
+-define(tTransportException_ALREADY_OPEN, 2).
+-define(tTransportException_TIMED_OUT, 3).
+-define(tTransportException_END_OF_FILE, 4).
+
+-record(tTransportException, {type, message}).