1# -*- python -*-
2#                           Package   : omniidl
3# output.py                 Created on: 1999/10/27
4#			    Author    : Duncan Grisby (dpg1)
5#
6#    Copyright (C) 2011 Apasphere Ltd
7#    Copyright (C) 1999 AT&T Laboratories Cambridge
8#
9#  This file is part of omniidl.
10#
11#  omniidl is free software; you can redistribute it and/or modify it
12#  under the terms of the GNU General Public License as published by
13#  the Free Software Foundation; either version 2 of the License, or
14#  (at your option) any later version.
15#
16#  This program is distributed in the hope that it will be useful,
17#  but WITHOUT ANY WARRANTY; without even the implied warranty of
18#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19#  General Public License for more details.
20#
21#  You should have received a copy of the GNU General Public License
22#  along with this program.  If not, see http://www.gnu.org/licenses/
23#
24# Description:
25#
26#   IDL compiler output functions
27
28"""Output stream
29
30Class:
31
32  Stream -- output stream which outputs templates, performing
33            key/value substitution and indentation."""
34
35def dummy(): pass
36
37StringType = type("")
38FuncType   = type(dummy)
39
40class Stream:
41    """IDL Compiler output stream class
42
43The output stream takes a template string containing keys enclosed in
44'@' characters and replaces the keys with their associated values. It
45also provides counted indentation levels.
46
47  eg. Given the template string:
48
49    template = \"\"\"\\
50class @id@ {
51public:
52  @id@(@type@ a) : a_(a) {}
53
54private:
55  @type@ a_;
56};\"\"\"
57
58  Calling s.out(template, id="foo", type="int") results in:
59
60    class foo {
61    public:
62      foo(int a) : a_(a) {}
63
64    private:
65      int a_;
66    };
67
68
69Functions:
70
71  __init__(file, indent_size)   -- Initialise the stream with the
72                                   given file and indent size.
73  inc_indent()                  -- Increment the indent level.
74  dec_indent()                  -- Decrement the indent level.
75  out(template, key=val, ...)   -- Output the given template with
76                                   key/value substitution and
77                                   indenting.
78  niout(template, key=val, ...) -- As out(), but with no indenting."""
79
80
81    def __init__(self, file, indent_size = 2):
82        self.file        = file
83        self.indent_size = indent_size
84        self.indent      = 0
85        self.do_indent   = 1
86
87    def inc_indent(self): self.indent = self.indent + self.indent_size
88    def dec_indent(self): self.indent = self.indent - self.indent_size
89
90    def out(self, text, ldict={}, **dict):
91        """Output a multi-line string with indentation and @@ substitution."""
92
93        dict.update(ldict)
94
95        pos    = 0
96        tlist  = text.split("@")
97        ltlist = len(tlist)
98        i      = 0
99        while i < ltlist:
100
101            # Output plain text
102            pos = self.olines(pos, self.indent, tlist[i])
103
104            i = i + 1
105            if i == ltlist: break
106
107            # Evaluate @ expression
108            try:
109                expr = dict[tlist[i]]
110            except:
111                # If a straight look-up failed, try evaluating it
112                if tlist[i] == "":
113                    expr = "@"
114                else:
115                    expr = eval(tlist[i], globals(), dict)
116
117            if type(expr) is StringType:
118                pos = self.olines(pos, pos, expr)
119            elif type(expr) is FuncType:
120                oindent = self.indent
121                self.indent = pos
122                apply(expr)
123                self.indent = oindent
124            else:
125                pos = self.olines(pos, pos, str(expr))
126
127            i = i + 1
128
129        self.odone()
130
131    def niout(self, text, ldict={}, **dict):
132        """Output a multi-line string without indentation."""
133
134        dict.update(ldict)
135
136        pos    = 0
137        tlist  = text.split("@")
138        ltlist = len(tlist)
139        i      = 0
140        while i < ltlist:
141
142            # Output plain text
143            pos = self.olines(pos, 0, tlist[i])
144
145            i = i + 1
146            if i == ltlist: break
147
148            # Evaluate @ expression
149            try:
150                expr = dict[tlist[i]]
151            except:
152                # If a straight look-up failed, try evaluating it
153                if tlist[i] == "":
154                    expr = "@"
155                else:
156                    expr = eval(tlist[i], globals(), dict)
157
158            if type(expr) is StringType:
159                pos = self.olines(pos, pos, expr)
160            elif type(expr) is FuncType:
161                oindent = self.indent
162                self.indent = pos
163                apply(expr)
164                self.indent = oindent
165            else:
166                pos = self.olines(pos, pos, str(expr))
167
168            i = i + 1
169
170        self.odone()
171
172
173    def olines(self, pos, indent, text):
174        istr  = " " * indent
175        write = self.file.write
176
177        stext = text.split("\n")
178        lines = len(stext)
179        line  = stext[0]
180
181        if self.do_indent:
182            pos = indent
183            write(istr)
184
185        write(line)
186
187        for i in range(1, lines):
188            line = stext[i]
189            write("\n")
190            if line:
191                pos = indent
192                write(istr)
193                write(line)
194
195        if lines > 1 and not line: # Newline at end of text
196            self.do_indent = 1
197            return self.indent
198
199        self.do_indent = 0
200        return pos + len(line)
201
202    def odone(self):
203        self.file.write("\n")
204        self.do_indent = 1
205
206
207class StringStream(Stream):
208    """Writes to a string buffer rather than a file."""
209    def __init__(self, indent_size = 2):
210        Stream.__init__(self, self, indent_size)
211        self.buffer = []
212
213    def write(self, text):
214        self.buffer.append(text)
215
216    def __str__(self):
217        return "".join(self.buffer)
218