/[eiffelstudio]/branches/eth/eve/Src/Eiffel/API/error/common/composite_formatter.e
ViewVC logotype

Contents of /branches/eth/eve/Src/Eiffel/API/error/common/composite_formatter.e

Parent Directory Parent Directory | Revision Log Revision Log


Revision 94983 - (show annotations)
Fri May 2 11:05:28 2014 UTC (5 years, 5 months ago) by jasonw
File size: 11024 byte(s)
<<Merged from trunk#94978.>>
1 ´╗┐note
2 description: "[
3 A formatter that injects objects of arbitrary types into a string message.
4 A formal parameter of the class denotes a type of the underlying formatter.
5 Argument placeholders in a string message are denoted by {n}
6 where n stands for an argument number n (starting from 1).
7 The same argument number can be used multiple times.
8
9 Example. Assuming there is a function as_injectable that converts its arguments to an injectable list,
10 format ("{1} has {3} apples, all {3} are {2}", as_injectable ("Bob", "green", 8))
11 the generated message looks like "Bob has 8 apples, all 8 are green".
12 ]"
13 syntax: "[
14 The following characters have special meaning:
15 { (opening brace) - beginning of an argument placeholder
16 } (closing brace) - end of an argument placeholder
17 | (vertical bar) - separator between argument number and format specification
18 ` (backquote) - escape character
19 Escape character is discarded from the original string and removes any special meaning of the character that follows it:
20 `` - denotes a single character ` without any special meaning,
21 `{ - denotes a single character { without any special meaning, etc.
22 An argument placeholder for an argument number n is denoted in a message string by one of the following sequences:
23 {n} - a placeholder with default format
24 {n|f} - a placeholder with (possibly empty) format specification f
25 The format specification f (if specified) or void (if there is no format specofication) is passed to the corresponding argument injector.
26 ]"
27
28 class COMPOSITE_FORMATTER [G]
29
30 create
31 make
32
33 feature {NONE} -- Creation
34
35 make (format_substring: PROCEDURE [ANY, TUPLE [G, READABLE_STRING_GENERAL, INTEGER_32, INTEGER_32]])
36 -- Associate formatter with the procedure `format_substring' to render message string elements.
37 do
38 format_message := format_substring
39 end
40
41 feature -- Formatting
42
43 format (formatter: G; message: READABLE_STRING_GENERAL; arguments: ITERABLE [PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G]]])
44 -- Format `message' replacing placeholders with elements from `arguments' using `formatter'.
45 -- Placeholders are denoted by {n} where n indicates an argument position.
46 local
47 a: detachable ARRAYED_LIST [PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G]]]
48 i: INTEGER
49 j: INTEGER
50 n: INTEGER
51 nesting_level: NATURAL_32
52 has_number: BOOLEAN
53 argument_number: INTEGER_32
54 cursor: ITERATION_CURSOR [PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G]]]
55 state: NATURAL_32
56 do
57 from
58 i := 1
59 n := message.count
60 state := state_message
61 j := 1
62 until
63 j > n
64 loop
65 inspect state
66 when state_message then
67 -- Regular message string between `i' and `j - 1'.
68 inspect message [j]
69 when escape_char then
70 -- Emit message collected so far.
71 if j > i then
72 format_message (formatter, message, i, j - 1)
73 end
74 -- Do not interpret next character.
75 j := j + 1
76 -- Advance to the next part.
77 i := j
78 when start_spec then
79 -- Emit message collected so far.
80 if j > i then
81 format_message (formatter, message, i, j - 1)
82 end
83 -- Increase nesting level.
84 nesting_level := nesting_level + 1
85 -- Advance to the next part.
86 state := state_placeholder
87 i := j + 1
88 else
89 -- Continue collecting regular characters.
90 end
91 when state_placeholder then
92 -- Beginning of a placeholder.
93 inspect message [j]
94 when '0' .. '9' then
95 -- There is an argument number.
96 has_number := True
97 state := state_number
98 else
99 -- Bad format, skip it.
100 has_number := False
101 state := state_format
102 end
103 -- Process the current character in a new state.
104 j := j - 1
105 when state_number then
106 -- Argument number.
107 inspect message [j]
108 when '0' .. '9' then
109 -- Read argument number.
110 argument_number := argument_number * 10 + (message [j].code - zero_code)
111 -- Check for overflow.
112 if argument_number < 0 then
113 -- Bad format, skip it.
114 has_number := False
115 argument_number := 0
116 state := state_format
117 end
118 when middle_spec then
119 -- Advance to the format specification.
120 i := j + 1
121 state := state_format
122 when stop_spec then
123 -- Advance to the empty format specification.
124 i := j + 1
125 state := state_format
126 -- Process the current character in a new state.
127 j := j - 1
128 else
129 -- Bad format, skip it.
130 has_number := False
131 argument_number := 0
132 state := state_format
133 end
134 when state_format then
135 -- Format specification.
136 inspect message [j]
137 when start_spec then
138 -- Nested placeholder.
139 nesting_level := nesting_level + 1
140 when stop_spec then
141 -- Decrease nesting level and check if this is the end of the current format specification.
142 nesting_level := nesting_level - 1
143 if nesting_level = 0 then
144 -- This is the end of the current format specification.
145 -- Ignore invalid placeholders.
146 if has_number and then argument_number > 0 then
147 -- This is a valid format specification, replace it with a formatted argument.
148 if not attached a or else not attached cursor then
149 -- Avoid allocating too much space in case there is an error in the argument specification.
150 if argument_number < 10 then
151 create a.make (argument_number)
152 else
153 create a.make (10)
154 end
155 -- Initialize iteration.
156 cursor := arguments.new_cursor
157 end
158 -- Compute argument at position `argument_number' if possible and store it to `a'.
159 compute_argument (a, cursor, argument_number)
160 -- Check if an argument is found.
161 if a.valid_index (argument_number) and then attached a [argument_number] as append then
162 -- Check if there is a format specification.
163 if i <= j then
164 -- Use given format specification.
165 append (create {FORMAT_SPECIFICATION}.make (message.substring (i, j - 1)), formatter)
166 else
167 -- Use default format.
168 append (Void, formatter)
169 end
170 end
171 end
172 end
173 -- Prepare for a regular message.
174 i := j + 1
175 state := state_message
176 when escape_char then
177 -- Do not interpret next character.
178 j := j + 1
179 else
180 -- Continue collecting format specification.
181 end
182 end
183 -- Advance to the next character.
184 j := j + 1
185 end
186 if state = state_message and then i <= n then
187 -- Emit last message.
188 format_message (formatter, message, i, n)
189 end
190 end
191
192 feature {NONE} -- State of a parser
193
194 state_message: NATURAL_32 = 0
195 -- State of message collection.
196
197 state_placeholder: NATURAL_32 = 1
198 -- State of placeholder parsing.
199
200 state_number: NATURAL_32 = 2
201 -- State of number parsing.
202
203 state_format: NATURAL_32 = 3
204 -- State of format specification parsing.
205
206 feature {NONE} -- Argument evaluation
207
208 compute_argument
209 (storage: detachable ARRAYED_LIST [PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G]]];
210 cursor: ITERATION_CURSOR [PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G]]];
211 argument_number: INTEGER_32)
212 -- Compute argument at position `argument_number' if possible using `cursor' and put it to `storage'.
213 local
214 n: INTEGER_32
215 do
216 -- Check if argument has been computed.
217 n := storage.count
218 if argument_number > n and then not cursor.after then
219 -- Argument has not been computed yet.
220 -- The loop cannot be replaced with an iteration form because the cursor is reused.
221 from
222 until
223 n = argument_number or else cursor.after
224 loop
225 -- Place next argument at the unallocated index.
226 storage.extend (cursor.item)
227 -- Update number of arguments.
228 n := n + 1
229 -- Advance to next argument.
230 cursor.forth
231 end
232 end
233 end
234
235 feature -- Constructor
236
237 list (add_list: PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G, ITERABLE [PROCEDURE [ANY, TUPLE [G]]]]]; items: ITERABLE [PROCEDURE [ANY, TUPLE [G]]]): PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G]]
238 -- New list composed by `add_list' from given `items' that can be passed as an element to `format'.
239 -- For example, "t.format ("{1}", [t.list (..., foo)])" will process "foo" as a list to be injected at position "{1}".
240 -- If "foo" were an iterable object, the call "t.format ("{1}", [foo])" will inject only the first item of "foo" instead.
241 do
242 Result := agent (s: detachable FORMAT_SPECIFICATION; a: PROCEDURE [ANY, TUPLE [detachable FORMAT_SPECIFICATION, G, ITERABLE [PROCEDURE [ANY, TUPLE [G]]]]]; i: ITERABLE [PROCEDURE [ANY, TUPLE [G]]]; t: G)
243 do
244 a (s, t, i)
245 end (?, add_list, items, ?)
246 end
247
248 feature {NONE} -- Access
249
250 format_message: PROCEDURE [ANY, TUPLE [f: G; s: READABLE_STRING_GENERAL; first: INTEGER_32; last: INTEGER_32]]
251 -- Formatter to be used for rendering message `s' from index `first' to index `last' using formatter `f'..
252
253 escape_char: CHARACTER_32 = '`'
254 -- Escape character.
255
256 start_spec: CHARACTER_32 = '{'
257 -- Character used to strart placeholder.
258
259 stop_spec: CHARACTER_32 = '}'
260 -- Character used to stop placeholder.
261
262 middle_spec: CHARACTER_32 = '|'
263 -- Character used to delimit argument number associated with a placeholder from the format specification string.
264
265 zero_code: INTEGER_32 = 48
266 -- Code of the character '0'.
267
268 invariant
269 zero_code_definition: zero_code = ('0').code
270
271 note
272 date: "$Date$"
273 revision: "$Revision$"
274 copyright: "Copyright (c) 1984-2014, Eiffel Software"
275 license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)"
276 licensing_options: "http://www.eiffel.com/licensing"
277 copying: "[
278 This file is part of Eiffel Software's Eiffel Development Environment.
279
280 Eiffel Software's Eiffel Development Environment is free
281 software; you can redistribute it and/or modify it under
282 the terms of the GNU General Public License as published
283 by the Free Software Foundation, version 2 of the License
284 (available at the URL listed under "license" above).
285
286 Eiffel Software's Eiffel Development Environment is
287 distributed in the hope that it will be useful, but
288 WITHOUT ANY WARRANTY; without even the implied warranty
289 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
290 See the GNU General Public License for more details.
291
292 You should have received a copy of the GNU General Public
293 License along with Eiffel Software's Eiffel Development
294 Environment; if not, write to the Free Software Foundation,
295 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
296 ]"
297 source: "[
298 Eiffel Software
299 5949 Hollister Ave., Goleta, CA 93117 USA
300 Telephone 805-685-1006, Fax 805-685-6869
301 Website http://www.eiffel.com
302 Customer support http://support.eiffel.com
303 ]"
304 end

Properties

Name Value
svn:eol-style native
svn:keywords Author Date ID Revision

  ViewVC Help
Powered by ViewVC 1.1.23