author | mkobetic |
Mon, 30 Jan 2012 22:33:34 +0000 | |
changeset 74 | 752e2d88fe73 |
parent 55 | e44717ae3e6c |
child 94 | 7e6a328c5f8b |
permissions | -rw-r--r-- |
2 | 1 |
"{ Package: 'stx:goodies/xtreams/core' }" |
2 |
||
3 |
"{ NameSpace: Xtreams }" |
|
4 |
||
5 |
Object subclass:#WriteStream |
|
6 |
instanceVariableNames:'destination' |
|
7 |
classVariableNames:'Backspace Bell CarriageReturn Delete DoubleQuote Escape FormFeed |
|
8 |
LineFeed Quote Space Tab VerticalTab' |
|
36 | 9 |
poolDictionaries:'XtreamsPool' |
20
51de794993c3
added XtreamsPool to fix DefaultBufferSize; set proper category names
mkobetic
parents:
7
diff
changeset
|
10 |
category:'Xtreams-Core' |
2 | 11 |
! |
12 |
||
13 |
WriteStream comment:'Abstract superclass of all write streams; defines the API. |
|
14 |
||
15 |
Write streams are created by sending #writing to a concrete resource (a.k.a terminal), such as a Collection, SocketAccessor, Filename, etc. |
|
16 |
||
17 |
String new writing write: ''testing''; close; terminal |
|
18 |
||
19 |
Transform write streams are created through one of the messages in the ''transforming'' protocol sent to other write streams. |
|
20 |
||
21 |
(String new writing collecting: #asUppercase) write: ''testing''; close; terminal |
|
22 |
||
23 |
Subclasses must implement the following messages: |
|
24 |
#read:into:at: |
|
25 |
#contentsSpecies |
|
26 |
||
27 |
Instance Variables |
|
28 |
destination <Object> a stream or "terminal" consuming written elements |
|
29 |
||
30 |
Shared Variables |
|
31 |
Backspace <Character> |
|
32 |
Bell <Character> |
|
33 |
CarriageReturn <Character> |
|
34 |
Delete <Character> |
|
35 |
DoubleQuote <Character> |
|
36 |
Escape <Character> |
|
37 |
FormFeed <Character> |
|
38 |
LineFeed <Character> |
|
39 |
Quote <Character> |
|
40 |
Space <Character> |
|
41 |
Tab <Character> |
|
42 |
VerticalTab <Character> |
|
43 |
||
44 |
' |
|
45 |
! |
|
46 |
||
47 |
||
48 |
!WriteStream class methodsFor:'instance creation'! |
|
49 |
||
50 |
on: aDestination |
|
51 |
^self new on: aDestination |
|
52 |
! ! |
|
53 |
||
54 |
!WriteStream class methodsFor:'class initialization'! |
|
55 |
||
56 |
initialize |
|
57 |
Backspace := String with: Character backspace. |
|
58 |
Bell := String with: (Character value: 7). |
|
59 |
CarriageReturn := String with: (Character value: 13). |
|
60 |
Delete := String with: (Character value: 127). |
|
61 |
DoubleQuote := String with: $". |
|
62 |
Escape := String with: (Character value: 27). |
|
63 |
FormFeed := String with: Character newPage. |
|
64 |
LineFeed := String with: Character lf. |
|
65 |
Quote := String with: $'. |
|
66 |
Space := String with: Character space. |
|
67 |
Tab := String with: Character tab. |
|
68 |
VerticalTab := String with: (Character value: 11) |
|
69 |
! ! |
|
70 |
||
71 |
!WriteStream methodsFor:'accessing'! |
|
72 |
||
73 |
conclusion |
|
74 |
"Close the stream and return the object at the bottom of the stream." |
|
75 |
" ^<Collection | Buffer | IOAccessor | BlockClosure> " |
|
76 |
self close. |
|
77 |
^self terminal |
|
78 |
! |
|
79 |
||
80 |
destination |
|
81 |
||
82 |
^destination |
|
83 |
! |
|
84 |
||
85 |
insert: aStreamable |
|
86 |
"Insert aStreamable into self at current position." |
|
87 |
" aStreamable <SequenceableCollection | ReadStream | Buffer> the source to write in to the destination |
|
88 |
^<Integer> the number of elements written to the destination" |
|
89 |
" |
|
90 |
' World!!' copy writing insert: 'Hello' reading; -= 0; close; destination |
|
91 |
" |
|
92 |
^aStreamable streamingInsertInto: self |
|
93 |
! |
|
94 |
||
95 |
insert: anInteger from: aStreamable |
|
96 |
"Insert anIntegers worth of elements from aStreamable into self at current position." |
|
97 |
" anInteger <Integer> the number of elements to insert |
|
98 |
aStreamable <ReadStream | SequenceableCollection | Buffer > the source to write into the destination |
|
99 |
startIndex <Integer> the index into aSequenceableCollection to start writing from |
|
100 |
^<Integer> number of elements inserted |
|
101 |
"" |
|
102 |
' World!!' copy writing insert: 5 from: 'Hello Underworld!!' reading; -= 0; close; destination |
|
103 |
" |
|
104 |
aStreamable streamingInsert: anInteger into: self. |
|
105 |
^anInteger |
|
106 |
! |
|
107 |
||
108 |
insert: anInteger from: aSequenceableCollection at: startIndex |
|
109 |
"Insert anIntegers worth of elements from aSequenceableCollection starting at startIndex into self at current position." |
|
110 |
" anInteger <Integer> the number of elements to insert |
|
111 |
aStreamable <SequenceableCollection> the source to write into the destination |
|
112 |
startIndex <Integer> the index into aSequenceableCollection to start writing from |
|
113 |
^<Integer> number of elements inserted |
|
114 |
"" |
|
115 |
' World!!' copy writing insert: 5 from: 'Hello' at: 1; -= 0; close; destination |
|
116 |
" |
|
117 |
self write: anInteger from: aSequenceableCollection at: startIndex. |
|
118 |
^anInteger |
|
119 |
! |
|
120 |
||
121 |
put: anObject |
|
122 |
"Write anObject into self." |
|
123 |
" anObject <Object> the object to write in to the destination |
|
124 |
^ <Object> the object that was written to the destination |
|
125 |
"" |
|
126 |
String new writing put: $h; close; destination |
|
127 |
" |
|
128 |
| cache | |
|
129 |
cache := self contentsSpecies newRecycled: 1. |
|
130 |
cache at: 1 put: anObject. |
|
131 |
self write: 1 from: cache at: 1. |
|
132 |
cache recycle. |
|
133 |
^anObject |
|
134 |
! |
|
135 |
||
136 |
terminal |
|
137 |
"Return the object at the bottom of the stream." |
|
138 |
" ^<Collection | Buffer | IOAccessor | BlockClosure> |
|
139 |
" |
|
140 |
^(destination isKindOf: WriteStream) |
|
141 |
ifTrue: [ destination terminal ] |
|
142 |
ifFalse: [ destination ] |
|
143 |
! |
|
144 |
||
145 |
write: aStreamable |
|
146 |
"Write aStreamable into self." |
|
147 |
" aStreamable <SequenceableCollection | ReadStream | Buffer> the source to write in to the destination |
|
148 |
^<Integer> the number of elements written to the destination" |
|
149 |
" |
|
150 |
String new writing write: 'Hello' reading; close; destination |
|
151 |
" |
|
152 |
^aStreamable streamingWriteInto: self |
|
153 |
! |
|
154 |
||
155 |
write: anInteger from: aStreamable |
|
156 |
"Write anInteger's worth of elements from aStreamable into self." |
|
157 |
" anInteger <Integer> the number of elements to write |
|
158 |
aStreamable <SequenceableCollection | ReadStream | Buffer> the source to write in to the destination |
|
159 |
^<Integer> number of elements written |
|
160 |
"" |
|
161 |
String new writing write: 3 from: 'Hello' reading; close; destination |
|
162 |
" |
|
163 |
^aStreamable streamingWrite: anInteger into: self |
|
164 |
! |
|
165 |
||
166 |
write: anInteger from: aSequenceableCollection at: startIndex |
|
167 |
"Write anIntegers worth of elements from aSequenceableCollection starting at startIndex into self." |
|
168 |
" anInteger <Integer> the number of elements to write |
|
169 |
aStreamable <SequenceableCollection> the source to write in to the destination |
|
170 |
startIndex <Integer> the index into aSequenceableCollection to start writing from |
|
171 |
^<Integer> number of elements written |
|
172 |
"" |
|
173 |
String new writing write: 3 from: 'Hello' at: 2; close; destination |
|
174 |
" |
|
175 |
^self subclassResponsibility |
|
176 |
! ! |
|
177 |
||
178 |
!WriteStream methodsFor:'characters'! |
|
179 |
||
180 |
backspace |
|
181 |
self write: Backspace |
|
182 |
! |
|
183 |
||
184 |
bell |
|
185 |
self write: Bell |
|
186 |
! |
|
187 |
||
188 |
cr |
|
189 |
self write: CarriageReturn |
|
190 |
! |
|
191 |
||
192 |
delete |
|
193 |
self write: Delete |
|
194 |
! |
|
195 |
||
196 |
escape |
|
197 |
self write: Escape |
|
198 |
! |
|
199 |
||
200 |
ff |
|
201 |
self write: FormFeed |
|
202 |
! |
|
203 |
||
204 |
lf |
|
205 |
self write: LineFeed |
|
206 |
! |
|
207 |
||
208 |
print: anObject |
|
209 |
anObject streamingPrintOn: self |
|
210 |
! |
|
211 |
||
212 |
q |
|
213 |
self write: Quote |
|
214 |
! |
|
215 |
||
216 |
||
217 |
self write: DoubleQuote |
|
218 |
! |
|
219 |
||
220 |
space |
|
221 |
self write: Space |
|
222 |
! |
|
223 |
||
224 |
space: anInteger |
|
225 |
anInteger timesRepeat: [self space] |
|
226 |
! |
|
227 |
||
228 |
tab |
|
229 |
self write: Tab |
|
230 |
! |
|
231 |
||
232 |
tab: anInteger |
|
233 |
anInteger timesRepeat: [self tab] |
|
234 |
! |
|
235 |
||
236 |
vtab |
|
237 |
self write: VerticalTab |
|
238 |
! ! |
|
239 |
||
240 |
!WriteStream methodsFor:'converting'! |
|
241 |
||
242 |
writing |
|
243 |
^[:object | self nextPut: object] writing |
|
244 |
contentsSpecies: self contentsSpecies; |
|
245 |
yourself |
|
246 |
! ! |
|
247 |
||
248 |
!WriteStream methodsFor:'initialize-release'! |
|
249 |
||
250 |
close |
|
251 |
"Close the destination from any more writes." |
|
252 |
||
253 |
self flush. |
|
254 |
destination close |
|
255 |
! |
|
256 |
||
257 |
contentsSpecies |
|
258 |
"The class of collection that is able to hold the kind of elements that this stream consumes." |
|
259 |
" ^ <Class> collection class |
|
260 |
" |
|
261 |
^self subclassResponsibility |
|
262 |
! |
|
263 |
||
264 |
flush |
|
265 |
"Make sure all the previously written elements are pushed down into the destination." |
|
266 |
destination flush |
|
267 |
! |
|
268 |
||
269 |
on: aDestination |
|
270 |
destination := aDestination |
|
271 |
! ! |
|
272 |
||
273 |
||
274 |
!WriteStream methodsFor:'printing'! |
|
275 |
||
276 |
printOn: aStream |
|
277 |
| stream | |
|
278 |
stream := String new writing. |
|
279 |
self streamingPrintOn: stream. |
|
280 |
aStream nextPutAll: stream conclusion. |
|
281 |
aStream cr. |
|
282 |
destination printOn: aStream. |
|
283 |
! |
|
284 |
||
285 |
streamingPrintOn: aStream |
|
286 |
aStream write: self class name |
|
287 |
! ! |
|
288 |
||
289 |
!WriteStream methodsFor:'private'! |
|
290 |
||
36 | 291 |
nextPut: anObject |
292 |
"This is here for compatibility with the existing StreamEncoders so that they can be re-used with transformation streams for encoding." |
|
293 |
self put: anObject. |
|
294 |
^anObject |
|
295 |
! |
|
296 |
||
297 |
nextPutAll: aCollection |
|
298 |
"This is here for compatibility with the existing StreamEncoders so that they can be re-used with transformation streams for encoding." |
|
299 |
self write: aCollection. |
|
300 |
^aCollection |
|
301 |
! |
|
302 |
||
2 | 303 |
streamingInsert: anInteger from: aReadStream |
29 | 304 |
| cache count | |
305 |
cache := self contentsSpecies newRecycled: (anInteger max: DefaultBufferSize). |
|
306 |
count := [aReadStream read: anInteger into: cache at: 1. anInteger] on: Incomplete do: [ :ex | ex count ]. |
|
307 |
self insert: count from: cache at: 1. |
|
308 |
cache recycle. |
|
309 |
count < anInteger ifTrue: [(Incomplete count: count) raise] |
|
2 | 310 |
! |
311 |
||
312 |
streamingInsertFrom: aReadStream |
|
29 | 313 |
| count cache | |
314 |
count := 0. |
|
315 |
cache := self contentsSpecies newRecycled: DefaultBufferSize. |
|
316 |
[[aReadStream read: cache size into: cache at: 1] on: Incomplete do: [:exception | |
|
317 |
self insert: exception. |
|
318 |
cache recycle. |
|
319 |
^count + exception count]. |
|
320 |
self insert: cache size from: cache at: 1. |
|
321 |
count := count + cache size] repeat |
|
2 | 322 |
! |
323 |
||
324 |
streamingWrite: anInteger from: aReadStream |
|
29 | 325 |
| cache toDo continue amount | |
326 |
cache := self contentsSpecies newRecycled: DefaultBufferSize. |
|
327 |
toDo := anInteger. continue := true. |
|
328 |
[ continue and: [ toDo > 0 ] ] whileTrue: [ |
|
329 |
amount := [ aReadStream read: (cache size min: toDo) into: cache at: 1 ] on: Incomplete do: [ :ex | continue := false. ex count ]. |
|
330 |
self write: amount from: cache at: 1. |
|
331 |
toDo := toDo - amount ]. |
|
332 |
cache recycle. |
|
333 |
toDo > 0 ifTrue: [(Incomplete count: anInteger - toDo) raise]. |
|
334 |
^anInteger |
|
2 | 335 |
! |
336 |
||
337 |
streamingWriteFrom: aReadStream |
|
29 | 338 |
| count cache | |
339 |
count := 0. |
|
340 |
cache := self contentsSpecies newRecycled: DefaultBufferSize. |
|
341 |
[[aReadStream read: cache size into: cache at: 1] on: Incomplete do: [:exception | |
|
342 |
self write: exception. |
|
343 |
cache recycle. |
|
344 |
^count + exception count]. |
|
345 |
self write: cache size from: cache at: 1. |
|
346 |
count := count + cache size] repeat |
|
2 | 347 |
! ! |
348 |
||
349 |
!WriteStream methodsFor:'seeking'! |
|
350 |
||
351 |
++ anInteger |
|
352 |
"Seek forward by anInteger elements. The stream must be positionable." |
|
353 |
" anInteger <Integer> the number of elements to go forward by. |
|
354 |
^<Integer> the number of elements actually skipped |
|
355 |
" |
|
356 |
" |
|
357 |
'Hello Would' copy writing ++ 6; write: 'World'; close; destination |
|
358 |
" |
|
359 |
"Subclasses should reimplement this method if the stream is positionable." |
|
360 |
self isPositionable |
|
361 |
ifFalse: [Incomplete zero raise] |
|
362 |
ifTrue: [self subclassResponsibility] |
|
363 |
! |
|
364 |
||
365 |
+= anInteger |
|
366 |
"Seek from the start of the stream by anInteger elements. The stream must be positionable." |
|
367 |
" anInteger <Integer> The number of elements to go forward by." |
|
368 |
" |
|
369 |
String new writing write: 'Hello Would'; += 6; write: 'World'; close; destination |
|
370 |
" |
|
371 |
^self position: anInteger |
|
372 |
! |
|
373 |
||
374 |
-- anInteger |
|
375 |
"Seek backward by anInteger elements. The stream must be positionable." |
|
376 |
" anInteger <Integer> The number of elements to go back by." |
|
377 |
" |
|
378 |
String new writing write: 'helio'; -- 2; write: 'lo'; close; destination |
|
379 |
" |
|
380 |
"Subclasses should reimplement this method if the stream is positionable." |
|
381 |
self isPositionable |
|
382 |
ifFalse: [self error: 'This stream is not positionable.'] |
|
383 |
ifTrue: [self subclassResponsibility] |
|
384 |
! |
|
385 |
||
386 |
-= anInteger |
|
387 |
"Seek backwards from the end of the stream by anInteger elements. The stream must be positionable." |
|
388 |
" anInteger <Integer> The number of elements to go back by. |
|
389 |
^<Integer> the number of elements actually skipped" |
|
390 |
" |
|
391 |
'Hello Would' copy writing -= 3; write: 'rld'; close; terminal |
|
392 |
" |
|
393 |
| available | |
|
394 |
available := anInteger min: self length. |
|
395 |
self position: self length - available. |
|
396 |
available = anInteger ifTrue: [ ^anInteger ]. |
|
397 |
^(Incomplete count: available) raise |
|
398 |
! |
|
399 |
||
400 |
available |
|
401 |
"Return the number of elements from the current position to the end of the stream. The stream must be positionable." |
|
402 |
" ^ <Integer> the number of elements available" |
|
403 |
" |
|
404 |
String new writing write: 'Hello World'; -- 5; available |
|
405 |
" |
|
406 |
^self length - self position |
|
407 |
! |
|
408 |
||
409 |
explore: aBlock |
|
410 |
" Explore the stream within the block but return to where we started when the block completes. The stream must be positionable." |
|
411 |
" aBlock <BlockClosure> defines the exploration activity |
|
412 |
^ <Object> result of aBlock" |
|
413 |
" |
|
414 |
String new writing explore: [ :s | s write: 'Hello' ]; write: 'World'; close; destination |
|
415 |
" |
|
416 |
| position | |
|
417 |
position := self position. |
|
418 |
^[aBlock cull: self] ensure: [self position: position] |
|
419 |
! |
|
420 |
||
421 |
length |
|
422 |
"Return total length of the stream. The stream must be positionable." |
|
423 |
" ^ <Integer> the total number of elements in the stream. (position + available)" |
|
424 |
" |
|
425 |
'Hello World' copy writing ++ 5; length |
|
426 |
" |
|
427 |
"Subclasses should reimplement this method if the stream is positionable." |
|
428 |
^self isPositionable |
|
429 |
ifFalse: [self error: 'This stream is not positionable.'] |
|
430 |
ifTrue: [self subclassResponsibility] |
|
431 |
! |
|
432 |
||
433 |
position |
|
434 |
"Return current position of the stream. The stream must be positionable." |
|
435 |
" ^ <Integer> current position of the stream." |
|
436 |
" |
|
437 |
'Hello World' copy writing -= 5; position |
|
438 |
" |
|
439 |
"Subclasses should reimplement this method if the stream is positionable." |
|
440 |
^self isPositionable |
|
441 |
ifFalse: [self error: 'This stream is not positionable.'] |
|
442 |
ifTrue: [self subclassResponsibility] |
|
443 |
! |
|
444 |
||
445 |
position: anInteger |
|
446 |
"Change current position of the stream to anInteger. The stream must be positionable." |
|
447 |
" anInteger <Integer> current position of the stream." |
|
448 |
" |
|
449 |
'Hello Would' copy writing position: 6; write: 'World'; close; destination |
|
450 |
" |
|
451 |
"Subclasses should reimplement this method if the stream is positionable." |
|
452 |
self isPositionable |
|
453 |
ifFalse: [self error: 'This stream is not positionable.'] |
|
454 |
ifTrue: [self subclassResponsibility] |
|
455 |
! ! |
|
456 |
||
457 |
||
458 |
!WriteStream methodsFor:'testing'! |
|
459 |
||
460 |
isPositionable |
|
461 |
"Can this stream be positioned. Positionable streams come with extra API: #position, #position:, etc." |
|
462 |
||
463 |
^false |
|
464 |
! |
|
465 |
||
466 |
isReadable |
|
467 |
^false |
|
468 |
! |
|
469 |
||
470 |
isWritable |
|
471 |
^true |
|
472 |
! ! |
|
473 |
||
474 |
||
475 |
!WriteStream class methodsFor:'documentation'! |
|
476 |
||
477 |
version_SVN |
|
478 |
^ '$Id$' |
|
479 |
! ! |
|
480 |
||
481 |
WriteStream initialize! |