"======================================================================
|
|   Stream Method Definitions
|
|   $Revision: 1.7.5$
|   $Date: 2000/05/28 16:56:52$
|   $Author: pb$
|
 ======================================================================"


"======================================================================
|
| Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
| Written by Steve Byrne.
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
| 
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
| 
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LESSER.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02111-1307, USA.  
|
 ======================================================================"


Object subclass: #Stream
       instanceVariableNames: ''
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Streams'
!

Stream comment: 
'I am an abstract class that provides interruptable sequential access to
objects.  I can return successive objects from a source, or accept
successive objects and store them sequentially on a sink.  I provide
some simple iteration over the contents of one of my instances, and 
provide for writing collections sequentially.' !


!Stream methodsFor: 'accessing-reading'!

next
    "Return the next object in the receiver"
    self subclassResponsibility
!

next: anInteger
    "Return the next anInteger objects in the receiver"
    | stream |
    stream := WriteStream on: (self species new: anInteger).
    anInteger timesRepeat: [ stream nextPut: self next ].
    ^stream contents
!

nextMatchFor: anObject
    "Answer whether the next object is equal to anObject. Even if it does
     not, anObject is lost"
    ^anObject = self next
!

splitAt: anObject
    "Answer an OrderedCollection of parts of the receiver. A new (possibly
     empty) part starts at the start of the receiver, or after every
     occurrence of an object which is equal to anObject (as compared by
     #=)."
    | result |
    result := OrderedCollection new: 10.
    [ self atEnd ] whileFalse: [
	result addLast: (self upTo: anObject)
    ].
    ^result
!

contents
    "Answer the whole contents of the receiver, from the next object to
     the last"
    | stream |

    stream := WriteStream on: (self species new: 10).
    self do: [ :each | stream nextPut: each ].
    ^stream contents
! !



!Stream methodsFor: 'accessing-writing'!

nextPut: anObject
    "Write anObject to the receiver"
    self subclassResponsibility
!

nextPutAll: aCollection
    "Write all the objects in aCollection to the receiver"
    aCollection do: [ :element | self nextPut: element ].
    ^aCollection
!

next: anInteger put: anObject
    "Write anInteger copies of anObject to the receiver"
    anInteger timesRepeat: [ self nextPut: anObject ].
    ^anObject
! !



!Stream methodsFor: 'testing'!

atEnd
    "Answer whether the stream has got to an end"
    self subclassResponsibility
! !



!Stream methodsFor: 'enumerating'!

do: aBlock
    "Evaluate aBlock once for every object in the receiver"
    [self atEnd] whileFalse:
    	[aBlock value: self next ]

! !



!Stream methodsFor: 'basic'!

species
    ^Array
! !


!Stream methodsFor: 'character writing'!

cr
    "Store a cr on the receiver"
    self nextPut: Character cr
!

nl
    "Store a new line on the receiver"
    self nextPut: Character nl
!

crTab
    "Store a cr and a tab on the receiver"
    self cr.
    self tab
!

nlTab
    "Store a new line and a tab on the receiver"
    self nl.
    self tab
!

space
    "Store a space on the receiver"
    self nextPut: Character space
!

tab
    "Store a tab on the receiver"
    self nextPut: Character tab
! !


!Stream methodsFor: 'providing consistent protocols'!

close
    "Do nothing. This is provided for consistency with file streams"
! !


!Stream methodsFor: 'printing'!

print: anObject
    "Print anObject on the receiver by sending printOn: to anObject. This
     method is provided so that you can use cascading and obtain
     better-looking code"
    anObject printOn: self
! !



!Stream methodsFor: 'storing'!

store: anObject
    "Print Smalltalk code compiling to anObject on the receiver, by sending
     storeOn: to anObject. This method is provided so that you can use
     cascading and obtain better-looking code"
    anObject storeOn: self
! !


!Stream methodsFor: 'filing out'!

fileOut: aClass
    "File out aClass on the receiver. If aClass is not a metaclass, file out
     class and instance methods; if aClass is a metaclass, file out only the
     class methods"

    aClass fileOutOn: self
! !


!Stream methodsFor: 'PositionableStream methods'!
"I could implement them so that skip: and position: are not needed, so I
 include them here."

skipTo: anObject
    "Move the current position to after the next occurrence of anObject
    and return true if anObject was found.  If anObject doesn't exist, the 
    pointer is atEnd, and false is returned."

    [ self atEnd ] whileFalse: [
    	self next = anObject ifTrue: [ ^true ]
    ].
    ^false

!

skipToAll: aCollection
    "If there is a sequence of objects remaining in the stream that is
     equal to the sequence in aCollection, set the stream position just
     past that sequence and answer true. Else, set the stream position
     to its end and answer false."
    | buffer next nextSource pos |

    buffer := ReadWriteStream on: (self species new: 8).
    buffer truncate.
    [   [   nextSource := self nextWithBuffer: buffer.
	    nextSource isNil ifTrue: [ ^false ].
	    next := nextSource next.
	    next = aCollection first
	]   whileFalse.

	"Push back the first character of the (possible) match"
	nextSource == self ifTrue: [ buffer nextPut: next ].
	pos := buffer position.
	buffer skip: -1.

	aCollection allSatisfy: [ :each |
	    nextSource := self nextWithBuffer: buffer.
	    nextSource isNil ifTrue: [ ^false ].
	    next := nextSource next.
	    nextSource == self ifTrue: [ buffer nextPut: next ].
	    each = next
	].
    ]   whileFalse: [ buffer position: pos ].
    ^true
!

upToEnd
    "Answer every item in the collection on which the receiver is
     streaming, from the next one to the last"
    | ws |
    ws := WriteStream on: (self species new: 8).
    [ self atEnd ] whileFalse: [ ws nextPut: self next ].
    ^ws contents
!

nextLine
    "Returns a collection of the same type that the stream accesses, up to 
    but not including the object anObject.  Returns the entire rest of the 
    stream's contents if anObject is not present. "
    | next ws cr nl |
    ws := WriteStream on: (self species new: 40).
    cr := Character cr.
    nl := Character nl.
    [ self atEnd or: [ (next := self next) == cr | (next == nl) ] ]
	whileFalse: [ ws nextPut: next ].

    next == cr ifTrue: [ self peekFor: nl ].
    ^ws contents
!

upToAll: aCollection
    "If there is a sequence of objects remaining in the stream that is
     equal to the sequence in aCollection, set the stream position just
     past that sequence and answer the elements up to, but not including,
     the sequence. Else, set the stream position to its end and answer
     all the remaining elements."
    | buffer next nextSource pos ws first |

    buffer := ReadWriteStream on: (self species new: 8).
    buffer truncate.
    ws := WriteStream on: (self species new: 8).
    first := aCollection at: 1.
    [   [   nextSource := self nextWithBuffer: buffer.
	    nextSource isNil ifTrue: [ ^ws contents ].
	    ws nextPut: (next := nextSource next).
	    next = first
	]   whileFalse.

	"Push back the first character of the (possible) match"
	nextSource == self ifTrue: [ buffer nextPut: next ].
	pos := buffer position.
	buffer skip: -1.

	aCollection allSatisfy: [ :each |
	    nextSource := self nextWithBuffer: buffer.
	    nextSource isNil ifTrue: [ ^ws contents ].
	    next := nextSource next.
	    nextSource == self ifTrue: [ buffer nextPut: next ].
	    each = next
	].
    ]   whileFalse: [ buffer position: pos ].
    "Remove from the answered stream the first element of aCollection"
    ^ws
	skip: -1;
	contents
!

upTo: anObject
    "Returns a collection of the same type that the stream accesses, up to 
    but not including the object anObject.  Returns the entire rest of the 
    stream's contents if anObject is not present. "
    | next ws |
    ws := WriteStream on: (self species new: 8).
    [ self atEnd or: [ (next := self next) = anObject ] ] whileFalse: [
	ws nextPut: next
    ].
    ^ws contents
! !

!Stream methodsFor: 'private'!

nextWithBuffer: buffer
    "Answer whether the next character must be read from the receiver
     or from the read-ahead buffer contained in buffer (another Po-
     sitionableStream). If both the receiver and the buffer contain
     no data, nil is answered, else the correct stream is answered."
    ^buffer atEnd
	ifTrue: [
	    self atEnd ifTrue: [ nil ] ifFalse: [ self ]
	]
	ifFalse: [ buffer ]
! !