1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21
22%% usage:  pretty_format:term(Term) -> PNF list of characters
23%%
24%%  Note: this is usually used in expressions like:
25%%	io:format('~s\n',[pretty_format:term(Term)]).
26%%
27%%  Uses the following simple heuristics
28%%
29%%  1) Simple tuples are printed across the page
30%%     (Simple means *all* the elements are "flat")
31%%  2) The Complex tuple {Arg1, Arg2, Arg3,....} is printed thus:
32%%       {Arg1,
33%%          Arg2,
34%%          Arg3,
35%%          ...}
36%%  3) Lists are treated as for tuples
37%%  4) Lists of printable characters are treated as strings
38%%
39%%  This method seems to work reasonable well for {Tag, ...} type
40%%  data structures
41
42-module(asn1ct_pretty_format).
43
44-export([term/1]).
45
46-import(io_lib, [write/1, write_string/1]).
47
48term(Term) ->
49    element(2, term(Term, 0)).
50
51%%______________________________________________________________________
52%% pretty_format:term(Term, Indent} -> {Indent', Chars}
53%%   Format <Term> -- use <Indent> to indent the *next* line
54%%     Note: Indent' is a new indentaion level (sometimes printing <Term>
55%%     the next line to need an "extra" indent!).
56
57term([], Indent) ->
58    {Indent, [$[,$]]};
59term(L, Indent) when is_list(L) ->
60    case is_string(L) of
61	true ->
62	    {Indent, write_string(L)};
63	false ->
64	    case complex_list(L) of
65		true ->
66		    write_complex_list(L, Indent);
67		false ->
68		    write_simple_list(L, Indent)
69	    end
70    end;
71term(T, Indent) when is_tuple(T) ->
72    case complex_tuple(T) of
73	true ->
74	    write_complex_tuple(T, Indent);
75	false ->
76	    write_simple_tuple(T, Indent)
77    end;
78term(A, Indent) ->
79    {Indent, write(A)}.
80
81%%______________________________________________________________________
82%%  write_simple_list([H|T], Indent) -> {Indent', Chars}
83
84write_simple_list([H|T], Indent) ->
85    {_, S1} = term(H, Indent),
86    {_, S2} = write_simple_list_tail(T, Indent),
87    {Indent, [$[,S1|S2]}.
88
89write_simple_list_tail([H|T], Indent) ->
90    {_, S1} = term(H, Indent),
91    {_, S2} = write_simple_list_tail(T, Indent),
92    {Indent, [$,,S1| S2]};
93write_simple_list_tail([], Indent) ->
94    {Indent, "]"};
95write_simple_list_tail(Other, Indent) ->
96    {_, S} = term(Other, Indent),
97    {Indent, [$|,S,$]]}.
98
99%%______________________________________________________________________
100%%  write_complex_list([H|T], Indent) -> {Indent', Chars}
101
102write_complex_list([H|T], Indent) ->
103    {I1, S1} = term(H, Indent+1),
104    {_, S2} = write_complex_list_tail(T, I1),
105    {Indent, [$[,S1|S2]}.
106
107write_complex_list_tail([H|T], Indent) ->
108    {I1, S1} = term(H, Indent),
109    {_, S2} = write_complex_list_tail(T, I1),
110    {Indent, [$,,nl_indent(Indent),S1,S2]};
111write_complex_list_tail([], Indent) ->
112    {Indent, "]"};
113write_complex_list_tail(Other, Indent) ->
114    {_, S} = term(Other, Indent),
115    {Indent, [$|,S,$]]}.
116
117%%______________________________________________________________________
118%%  complex_list(List) -> true | false
119%%     returns true if the list is complex otherwise false
120
121complex_list([]) ->
122    false;
123complex_list([H|T]) when is_list(H) =:= false , is_tuple(H) =:= false ->
124    complex_list(T);
125complex_list([H|T]) ->
126    case is_string(H) of
127	true ->
128	    complex_list(T);
129	false ->
130	    true
131    end;
132complex_list(_) -> true.
133
134%%______________________________________________________________________
135%%  complex_tuple(Tuple) -> true | false
136%%     returns true if the tuple is complex otherwise false
137
138complex_tuple(T) ->
139    complex_list(tuple_to_list(T)).
140
141%%______________________________________________________________________
142%%  write_simple_tuple(Tuple, Indent} -> {Indent', Chars}
143
144write_simple_tuple({}, Indent) ->
145    {Indent, "{}"};
146write_simple_tuple(Tuple, Indent) ->
147    {_, S} = write_simple_tuple_args(tuple_to_list(Tuple), Indent),
148    {Indent, [${, S, $}]}.
149
150write_simple_tuple_args([X], Indent) ->
151    term(X, Indent);
152write_simple_tuple_args([H|T], Indent) ->
153    {_, SH} = term(H, Indent),
154    {_, ST} = write_simple_tuple_args(T, Indent),
155    {Indent, [SH, $,, ST]}.
156
157%%______________________________________________________________________
158%%  write_complex_tuple(Tuple, Indent} -> {Indent', Chars}
159
160write_complex_tuple(Tuple, Indent) ->
161    [H|T] = tuple_to_list(Tuple),
162    {I1, SH} = term(H, Indent+2),
163    {_, ST} = write_complex_tuple_args(T, I1),
164    {Indent, [${, SH, ST, $}]}.
165
166write_complex_tuple_args([X], Indent) ->
167    {_, S} = term(X, Indent),
168    {Indent, [$,, nl_indent(Indent), S]};
169write_complex_tuple_args([H|T], Indent) ->
170    {I1, SH} = term(H, Indent),
171    {_, ST} = write_complex_tuple_args(T, I1),
172    {Indent, [$,, nl_indent(Indent) , SH, ST]};
173write_complex_tuple_args([], Indent) ->
174    {Indent, []}.
175
176%%______________________________________________________________________
177%% utilities
178
179nl_indent(I) when I >= 0 ->
180    ["\n"|indent(I)];
181nl_indent(_) ->
182    [$\s].
183
184indent(I) when I >= 8 ->
185    [$\t|indent(I-8)];
186indent(I) when I > 0 ->
187    [$\s|indent(I-1)];
188indent(_) ->
189    [].
190
191is_string([9|T]) ->
192    is_string(T);
193is_string([10|T]) ->
194    is_string(T);
195is_string([H|T]) when H >31, H < 127 ->
196    is_string(T);
197is_string([]) ->
198    true;
199is_string(_) ->
200    false.
201
202
203