'From Squeak2.8 of 23 June 2000 [latest update: #2342] on 7 July 2000 at 11:45:52 am'! "Change Set: streaming1g1JMM Date: 3 July 2000 Author: Craig Latta Changes by johnmci@smalltalkconsulting.com This is a reorganization of the Stream hierarchy. It eliminates a lot of redundant and twisty code, and does buffering For documentation see the original author's web pages at http://netjam.org/self/projects/smalltalk/ Changed by JMM to use the disney exception handlers. Do not file in over top of an older streaming application, bad things might happen. Usually of course you will file this in to use Craig's correspondents/flow classes. See the correspondents1b4JMM change set for more information For testing see the JMMSUnitsForFlow change set"! Smalltalk renameClassNamed: #Stream as: #OldStream! !OldStream commentStamp: '' prior: 0! Stream comment: 'I am an abstract class that represents an accessor for a sequence of objects. This sequence is referred to as my "contents".'! Smalltalk renameClassNamed: #PositionableStream as: #OldPositionableStream! !OldPositionableStream commentStamp: '' prior: 0! PositionableStream comment: 'I represent an accessor for a sequence of objects (a collection) that are externally named by indices so that the point of access can be repositioned. I am abstract in that I do not implement the messages next and nextPut: which are inherited from my superclass Stream.'! Object subclass: #Stream instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Collections-Streams'! !Stream commentStamp: '' prior: 0! I am an abstract Class. Each of my instances can access a sequence of Objects. Note that in this implementation, unlike most others, all streams are readable; we assume that write-only streams are never needed. One might imagine wanting to have a stream on a write-only external resource, but in that case a reading attempt will induce primitive failure. There is no need to introduce the "write-only" concept into the Class factoring. Author: Craig Latta ! Stream subclass: #PositionableStream instanceVariableNames: 'collection position readLimit ' classVariableNames: '' poolDictionaries: 'TextConstants ' category: 'Collections-Streams'! !PositionableStream commentStamp: '' prior: 0! My instances are streams on sequences for which positioning makes sense. Note the difference between "position" and "index" in system comments. A position is a zero-based number pointing just before an element. One may think of it as a "number of elements accessed" counter. An index is a one-based number referring to an element. Thus, a stream positioned before the first element has position zero, and the element has index one. instance variables: collection the collection to which an instance provides access position an instance''s current sequence position. readLimit the index of the last readable element in an instance''s collection Author: Craig Latta ! PositionableStream subclass: #WriteableStream instanceVariableNames: 'writeLimit ' classVariableNames: '' poolDictionaries: '' category: 'Collections-Streams'! !WriteableStream commentStamp: '' prior: 0! WriteableStream comment: ' My instances are writeable streams. instance variable: writeLimit the index of the last writeable slot in an instance''s collection Author: Craig Latta ' ! WriteableStream subclass: #DiscardingStream instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Collections-Streams'! !DiscardingStream commentStamp: '' prior: 0! DiscardingStream comment: ' My instances discard their elements as they are read. Author: Craig Latta' ! WriteableStream subclass: #ExternalStream instanceVariableNames: 'resource binary ' classVariableNames: '' poolDictionaries: '' category: 'Collections-Streams-External'! !ExternalStream commentStamp: '' prior: 0! I am an abstract Class. Instances of my concrete subclasses provide access to external stream resources. Author: Craig Latta ! ExternalStream subclass: #NetStream instanceVariableNames: 'transcribeTraffic lookBackStream numberOfBytesRead ' classVariableNames: '' poolDictionaries: '' category: 'Collections-Streams-External'! !NetStream commentStamp: '' prior: 0! NetStream comment: ' Each of my instances is a stream for a NetResource which implements streaming services. instance variables: transcribeTraffic a Boolean indicating whether an instance should write a transcript of its traffic activity lookBackStream a DiscardingStream on recently-received traffic Author: Craig Latta '! !Class methodsFor: 'initialize-release' stamp: 'crl 3/16/1999 15:27'! sharing: poolString "Set up sharedPools. Answer whether recompilation is advisable." | oldPools found | oldPools _ self sharedPools. sharedPools _ OrderedCollection new. (Scanner new scanFieldNames: poolString) do: [:poolName | sharedPools add: (Smalltalk at: poolName asSymbol ifAbsent: [Smalltalk at: poolName asSymbol put: Dictionary new])]. sharedPools isEmpty ifTrue: [sharedPools _ nil]. oldPools do: [:pool | found _ false. self sharedPools do: [:p | p == pool ifTrue: [found _ true]]. found ifFalse: [^ true "A pool got deleted"]]. ^ false! ! !Compiler methodsFor: 'private' stamp: 'crl 3/2/1999 16:59'! from: textOrStream class: aClass context: aContext notifying: req (textOrStream isKindOf: OldPositionableStream) ifTrue: [sourceStream _ textOrStream] ifFalse: [sourceStream _ ReadStream on: textOrStream asString]. class _ aClass. context _ aContext. requestor _ req! ! !Decompiler methodsFor: 'instruction decoding'! case: dist "statements = keyStmts CascadeFlag keyValueBlock ... keyStmts" | nextCase end thenJump stmtStream elements b node cases otherBlock | nextCase _ pc + dist. end _ limit. "Now add CascadeFlag & keyValueBlock to statements" statements addLast: stack removeLast; addLast: (self blockTo: nextCase). stack last == CascadeFlag ifFalse: "Last case" ["ensure jump is within block (in case thenExpr returns wierdly I guess)" thenJump _ exit <= end ifTrue: [exit] ifFalse: [nextCase]. stmtStream _ PositionableStream on: (self popTo: stack removeLast). elements _ OrderedCollection new. b _ OrderedCollection new. [stmtStream atEnd] whileFalse: [(node _ stmtStream nextOrNil) == CascadeFlag ifTrue: [elements addLast: (constructor codeMessage: (constructor codeBlock: b returns: false) selector: (constructor codeSelector: #-> code: #macro) arguments: (Array with: stmtStream next)). b _ OrderedCollection new] ifFalse: [b addLast: node]]. b size > 0 ifTrue: [self error: 'Bad cases']. cases _ constructor codeBrace: elements. otherBlock _ self blockTo: thenJump. stack addLast: (constructor codeMessage: stack removeLast selector: (constructor codeSelector: #caseOf:otherwise: code: #macro) arguments: (Array with: cases with: otherBlock))]! ! !HTMLformatter methodsFor: 'translating' stamp: 'crl 3/2/1999 16:45'! swikify: aStringOrStream linkhandler: aBlock | sourceStream aLine targetStream start end forbidden ignore | (aStringOrStream isKindOf: OldStream) ifTrue: [sourceStream := aStringOrStream] ifFalse: [sourceStream := ReadStream on: aStringOrStream]. forbidden _ self rangesOfAngleBrackets: sourceStream. targetStream := WriteStream on: String new. [sourceStream atEnd] whileFalse: [aLine := sourceStream upTo: (Character cr). " Now, look for links " start _ 1. [(start _ aLine indexOfSubCollection: (specialCharacter asString) startingAt: start ifAbsent: [0]) ~= 0 and: [start < aLine size]] whileTrue: [(aLine at: start+1) = specialCharacter ifTrue: [aLine _ aLine copyReplaceFrom: start to: start+1 with: specialCharacter asString. start_start + 1. ] ifFalse: [ (end _ aLine indexOfSubCollection: (specialCharacter asString) startingAt: (start+1) ifAbsent: [0]) ~= 0 ifTrue: [aLine _ aLine copyReplaceFrom: start to: end with: (aBlock value: (aLine copyFrom: start+1 to: end-1))] ifFalse: [start _ start + 1]]]. "If it's at least 4 dashes, make it a horizontal rule" (aLine indexOfSubCollection: '----' startingAt: 1) = 1 ifTrue: [targetStream nextPutAll: '
'] ifFalse: [targetStream nextPutAll: aLine]. "Should there be a
after this line?" (ignore _ sourceStream peek = $<) ifTrue: [ "If just before a tag, ignore the newline" targetStream nextPut: $ ]. "but do put in a separator" forbidden do: [:interval | (interval includes: sourceStream position) ifTrue: [ignore _ true]]. ignore ifFalse: [ (sourceStream peek) = (Character cr) ifTrue: [sourceStream next. targetStream nextPutAll: '

'; cr.] ifFalse: [targetStream nextPutAll: '
'; cr.]]]. ^targetStream contents. ! ! !HTMLformatter class methodsFor: 'translating' stamp: 'crl 3/2/1999 16:46'! fixEndings: aStringOrStream | sourceStream targetStream aLine | (aStringOrStream isKindOf: OldStream) ifTrue: [sourceStream := aStringOrStream] ifFalse: [sourceStream := ReadStream on: aStringOrStream]. targetStream := ReadWriteStream on: String new. [sourceStream atEnd] whileFalse: [aLine := sourceStream upTo: (Character linefeed). targetStream nextPutAll: aLine. targetStream nextPut: Character cr.]. ^targetStream ! ! !HTMLformatter class methodsFor: 'translating' stamp: 'crl 3/2/1999 16:46'! forEvaluatingEmbedded: stringOrStream "stringOrStream is text with expressions intermingled. This creates a HTLMLformatter instance which will substitute the expressions with the value of the argument (named request), and which leaves all other text in stringOrStream alone" | blockStream sourceStream doingEval ch | blockStream _ WriteStream on: String new. blockStream nextPutAll: '[ :request :output | output nextPutAll: '''. (stringOrStream isKindOf: OldStream) ifTrue: [sourceStream := stringOrStream] ifFalse: [sourceStream := ReadStream on: stringOrStream]. doingEval _ false. [sourceStream atEnd] whileFalse: [ ch := sourceStream next. (doingEval not and: [ ch = $< and: [ sourceStream peek = $? ]]) ifTrue: [ "beginning of an expression" blockStream nextPutAll: '''. output nextPutAll: ['. sourceStream next. "Skip the ?" doingEval _ true] ifFalse: [ (doingEval and: [ ch = $? and: [ sourceStream peek = $> ]]) ifTrue: [ "end of a expression" blockStream nextPutAll: '] value asString. output nextPutAll: '''. sourceStream next. "Skip the >" doingEval _ false.] ifFalse: [ "normal char" blockStream nextPut: ch. (doingEval not and: [ ch = $' ]) ifTrue: [ "double $' marks" blockStream nextPut: $' ] ] ] ]. "end the block" doingEval ifTrue: [ blockStream nextPutAll: '] value asString' ] ifFalse: [ blockStream nextPutAll: '''' ]. blockStream nextPutAll: ']'. ^HTMLformatter new formattingBlock: (Compiler evaluate: blockStream contents)! ! !HTMLformatter class methodsFor: 'translating' stamp: 'crl 3/2/1999 16:46'! oldEvalEmbedded: stringOrStream with: request | sourceStream targetStream evalStream currentStream evalValue peekValue ch | (stringOrStream isKindOf: OldStream) ifTrue: [sourceStream := stringOrStream] ifFalse: [sourceStream := ReadStream on: stringOrStream]. targetStream := WriteStream on: String new. currentStream := targetStream. [sourceStream atEnd] whileFalse: [ch := sourceStream next. ch = $< ifTrue: [ peekValue := sourceStream peek. (peekValue = $?) ifTrue: [evalStream := WriteStream on: String new. currentStream := evalStream. sourceStream next. "Eat the ?" ch := sourceStream next.]]. ((currentStream = evalStream) and: [ch = $?]) ifTrue: [ peekValue := sourceStream peek. (peekValue = $>) ifTrue: [sourceStream next. "Eat the >" currentStream := targetStream. evalValue := (Compiler new evaluate: (evalStream contents) in: thisContext to: self notifying: nil ifFail: [^nil]). (evalValue isKindOf: String) ifFalse: [evalValue := evalValue printString]. currentStream nextPutAll: evalValue.]] ifFalse: [currentStream nextPut: ch].]. ^targetStream contents ! ! !HTMLformatter class methodsFor: 'translating' stamp: 'crl 3/2/1999 16:46'! simpleProcess: aStringOrStream | sourceStream targetStream ch | (aStringOrStream isKindOf: OldStream) ifTrue: [sourceStream := aStringOrStream] ifFalse: [sourceStream := ReadStream on: aStringOrStream]. targetStream := WriteStream on: String new. [sourceStream atEnd] whileFalse: [ch := sourceStream next. (ch = Character linefeed) ifTrue: [(sourceStream peek) = (Character linefeed) ifTrue: [sourceStream next. targetStream nextPutAll: '

'] ifFalse: [targetStream nextPutAll: '
']]. targetStream nextPut: ch]. ^targetStream contents. ! ! !LessHTMLformatter methodsFor: 'translating' stamp: 'crl 3/2/1999 16:45'! swikify: aStringOrStream linkhandler: aBlock | sourceStream aLine targetStream start end forbidden ignore | (aStringOrStream isKindOf: OldStream) ifTrue: [sourceStream := aStringOrStream] ifFalse: [sourceStream := ReadStream on: aStringOrStream]. forbidden _ self rangesOfAngleBrackets: sourceStream. targetStream := WriteStream on: String new. [sourceStream atEnd] whileFalse: [aLine := sourceStream upTo: (Character cr). " Now, look for links " start _ 1. [(start _ aLine indexOfSubCollection: (specialCharacter asString) startingAt: start ifAbsent: [0]) ~= 0 and: [start < aLine size]] whileTrue: [(aLine at: start+1) = specialCharacter ifTrue: [aLine _ aLine copyReplaceFrom: start to: start+1 with: specialCharacter asString. start_start + 1.] ifFalse: [ (end _ aLine indexOfSubCollection: (specialCharacter asString) startingAt: (start+1) ifAbsent: [0]) ~= 0 ifTrue: [aLine _ aLine copyReplaceFrom: start to: end with: (aBlock value: (aLine copyFrom: start+1 to: end-1))] ifFalse: [start _ start + 1]]]. "If it's at least 4 dashes, make it a horizontal rule" (aLine indexOfSubCollection: '----' startingAt: 1) = 1 ifTrue: [targetStream nextPutAll: '


' ; cr.] ifFalse: [ (aLine beginsWith: '-') ifTrue: [targetStream nextPutAll: '
  • ',aLine allButFirst; cr.] ifFalse: [ (aLine beginsWith: '====') ifTrue: [targetStream nextPutAll: '

    ', (aLine copyFrom: 5 to: aLine size),'

    ';cr.] ifFalse: [ (aLine beginsWith: '===') ifTrue: [targetStream nextPutAll: '

    ', (aLine copyFrom: 4 to: aLine size),'

    ';cr.] ifFalse: [ (aLine beginsWith: '==') ifTrue: [targetStream nextPutAll: '

    ', (aLine copyFrom: 3 to: aLine size),'

    ';cr.] ifFalse: [ (aLine beginsWith: '=') ifTrue: [targetStream nextPutAll: '

    ', aLine allButFirst,'

    ';cr.] ifFalse: [ (aLine beginsWith: '!!') ifTrue: [targetStream nextPutAll: '',aLine allButFirst,'';cr.] ifFalse: [ ((aLine beginsWith: '|') and: [(aLine count: [:c | c= $|]) > 2]) "Then treat it as a table" ifTrue: [targetStream nextPutAll: '', (aLine allButFirst allButLast copyReplaceAll: '|' with: ''),''.] ifFalse: [targetStream nextPutAll: aLine].]]]]]]]. "Should there be a
    after this line?" (ignore _ sourceStream peek = $<) ifTrue: [ "If just before a tag, ignore the newline" targetStream nextPut: $ ]. "but do put in a separator" forbidden do: [:interval | (interval includes: sourceStream position) ifTrue: [ignore _ true]]. ignore ifFalse: [ (sourceStream peek) = (Character cr) ifTrue: [sourceStream next. targetStream nextPutAll: '

    '; cr.] ifFalse: [targetStream cr.].] ifTrue: [targetStream cr.].]. ^targetStream contents. ! ! !OldStream methodsFor: 'accessing' stamp: 'crl 3/2/1999 17:27'! nextOrNil "Answer the next object accessible by the receiver." ^self next! ! !SequenceableCollection methodsFor: 'stream creation'! emptyWriteableStream "Answer a writeable stream on me which assumes that I have nothing to read initially." ^WriteableStream emptyOn: self! ! !SequenceableCollection methodsFor: 'stream creation'! fullWriteableStream "Answer a writeable stream on me which assumes that I have something to read initially." ^WriteableStream fullOn: self! ! !SequenceableCollection methodsFor: 'stream creation'! stream "Answer a (readable, not writeable) stream on me." ^PositionableStream on: self! ! !SequenceableCollection methodsFor: 'stream creation'! writeableStream "Answer a writeable stream on me." ^WriteableStream on: self! ! !Stream methodsFor: 'accessing'! contents "Answer all of my readable elements." ^self subclassResponsibility! ! !Stream methodsFor: 'accessing'! newCollection "Answer a new Collection in which to collect my elements." ^OrderedCollection new ! ! !Stream methodsFor: 'accessing'! next "Answer the next element." ^self subclassResponsibility! ! !Stream methodsFor: 'accessing'! next: anInteger "Answer the next anInteger elements." | elements | elements _ self newCollection. anInteger timesRepeat: [elements addLast: self next]. ^elements! ! !Stream methodsFor: 'accessing'! next: anInteger do: aBlockClosure "Evalate aBlockClosure with each of the next anInteger elements." | index | index _ 1. [ index > anInteger or: [self atEnd] ] whileFalse: [ aBlockClosure value: self next. index _ index + 1]! ! !Stream methodsFor: 'accessing'! nextAvailable: anInteger "Answer the next available anInteger elements." ^self next: ( ((self size - self position) < anInteger) ifTrue: [self size - self position] ifFalse: [anInteger])! ! !Stream methodsFor: 'accessing'! size "Answer the number of readable elements I have." ^self subclassResponsibility! ! !Stream methodsFor: 'testing'! atEnd "Answer whether I can access any more elements." ^self subclassResponsibility! ! !Stream methodsFor: 'testing'! isEmpty "Answer whether I have no elements to read." ^self subclassResponsibility! ! !Stream methodsFor: 'backward compatibility' stamp: 'JMM 6/27/2000 17:59'! nextOrNil "Answer the next element, or nil if there isn't one." ^[self next] on: ExternalResource notEnoughElementsSignal do: [:exception | exception return: nil]! ! !Stream methodsFor: 'backward compatibility'! stream "Answer myself as a Stream." "This is used by the compiler, and should be removed." ^self! ! !Stream methodsFor: 'forward compatibility'! isExternal "Answer whether my collection is external." "This is used by Harmony." ^false! ! !Stream methodsFor: 'enumerating'! do: aBlockClosure "Evaluate aBlockClosure for each of my elements." [self atEnd] whileFalse: [aBlockClosure value: self next]! ! !Stream methodsFor: 'control'! close "For compatibility with external streams."! ! !Stream methodsFor: 'control'! commit "Commit any buffered data to persistent storage." "For compatibility with FileStreams." "By default, do nothing."! ! !PositionableStream methodsFor: 'source chunking'! copyChunkTo: aWriteStream "Copy the next chunk onto aWriteStream (must be different from the receiver) " | terminator | terminator _ $!!. self skipSeparators. aWriteStream nextPutAll: (self upTo: terminator); nextPut: terminator. [self peekFor: terminator] whileTrue: "case of imbedded (doubled) terminator" [aWriteStream nextPut: terminator; nextPutAll: (self upTo: terminator); nextPut: terminator]. ! ! !PositionableStream methodsFor: 'source chunking'! fileIn "This is special for reading expressions from text that has been formatted with exclamation delimitors. The expressions are read and passed to the Compiler. Answer the result of compilation." | val | Cursor read showWhile: [[self atEnd] whileFalse: [self skipSeparators. val _ (self peekFor: $!!) ifTrue: [(Compiler evaluate: self nextChunk logged: false) scanFrom: self] ifFalse: [Compiler evaluate: self nextChunk logged: true]]. self close]. ^val! ! !PositionableStream methodsFor: 'source chunking'! nextChunk "Answer the contents of the receiver, up to the next terminator character. Imbedded terminators are doubled." | terminator segment | terminator _ $!!. self skipSeparators. segment _ self upTo: terminator. [self peekFor: terminator] whileTrue: "case of imbedded (doubled) terminator" [segment _ (segment copyWith: terminator) , (self upTo: terminator)]. ^ segment! ! !PositionableStream methodsFor: 'source chunking'! nextChunkText ^ self nextChunk! ! !PositionableStream methodsFor: 'source chunking'! skipSeparators [self atEnd == false and: [self peek isSeparator]] whileTrue: [self next]! ! !PositionableStream methodsFor: 'source chunking'! skipStyleChunk "Get to the start of the next chunk that is not a style for the previous chunk" | pos | pos _ self position. self skipSeparators. self peek == $] ifTrue: [(self upTo: $[) = ']text' "old -- no longer needed" "now positioned past the open bracket" ifFalse: [self nextChunk]] "absorb ]style[ and its whole chunk" ifFalse: [self position: pos] "leave untouched" ! ! !PositionableStream methodsFor: 'nonhomogeneous accessing'! next16BitsPut: sixteenBitValue "Write sixteenBitValue at the current position." self nextPut: (sixteenBitValue bitShift: -8); nextPut: (sixteenBitValue bitAnd: 2r11111111)! ! !PositionableStream methodsFor: 'nonhomogeneous accessing'! nextByte "Assuming my collection is comprised of eight-bit bytes, answer the next one." ^self next asInteger! ! !PositionableStream methodsFor: 'nonhomogeneous accessing'! nextWord "Answer the next four bytes as an Integer." "Open-coded for speed." | word | word _ self next asInteger. word _ (word bitShift: 8) + self next asInteger. word _ (word bitShift: 8) + self next asInteger. word _ (word bitShift: 8) + self next asInteger. ^word! ! !PositionableStream methodsFor: 'nonhomogeneous accessing'! nextWordPut: aWord "Write aWord as the next four bytes." "Open-coded for speed." self nextPut: (Character value: ((aWord bitShift: -24) bitAnd: 255)). self nextPut: (Character value: ((aWord bitShift: -16) bitAnd: 255)). self nextPut: (Character value: ((aWord bitShift: -8) bitAnd: 255)). self nextPut: (Character value: (aWord bitAnd: 255)). ^aWord! ! !PositionableStream methodsFor: 'backward compatibility'! command: aString "This message is sent to the changes stream when writing out the source code of a method."! ! !PositionableStream methodsFor: 'backward compatibility'! endEntry "This is for compatibility between Transcript, File, and Stream."! ! !PositionableStream methodsFor: 'backward compatibility'! header "If the stream requires a standard header, override this message. See HtmlFileStream"! ! !PositionableStream methodsFor: 'backward compatibility'! nextWord16 "For compatibility with old systems only" "Answer the next two bytes from the receiver as an Integer." | high low str | true ifTrue: ["Slower code works while file next fails on non-ascii data" str _ self next: 2. high _ str at: 1. low _ str at: 2] ifFalse: [high _ self next. low _ self next]. ^(high asInteger bitShift: 8) + low asInteger! ! !PositionableStream methodsFor: 'backward compatibility'! trailer "If the stream requires a standard trailer, override this message. See HtmlFileStream"! ! !PositionableStream methodsFor: 'backward compatibility'! withClassNameAndMetaStateDo: twoArgumentBlock "Assume that my contents is a Class name. Answer the result of evaluating twoArgumentBlock. The first argument is whether the second word of my contents is 'class', that is, whether my contents name a Metaclass. The second argument is the first word of my contents as a Protoclass name. " | firstWord | self atEnd ifTrue: [^twoArgumentBlock value: nil value: false]. firstWord _ self nextWordOfCharacters asSymbol. ^self atEnd ifTrue: [ twoArgumentBlock value: ( firstWord isEmpty ifTrue: [nil] ifFalse: [firstWord] ) value: false ] ifFalse: [ twoArgumentBlock value: firstWord value: ( self nextWordOfCharacters = 'class' ifTrue: [true] ifFalse: [false] ) ]! ! !PositionableStream methodsFor: 'backward compatibility'! withClassNameMetaStateAndSelectorDo: threeArgumentBlock "Assume my contents are a method name. Answer the result of evaluating threeArgumentBlock. The first argument is the name of the indicated Protoclass. The second argument is whether the indicated Class is a Metaclass. The third argument is the indicated selector, or nil if no selector is indicated." "A contents of 'Object class' (for example) is ambiguous: it could be either the instance method Object>>class or it could be the metaclass of Object. Assume that if the contents has only two words, then it specifies the name of an instance method, even if the second word is 'class'. If the contents has three or more words, and the second word is 'class', then it specifies a class method." | firstWord secondWord | self atEnd ifTrue: [ ^threeArgumentBlock value: nil value: false value: nil ]. firstWord _ self nextWordOfCharacters. self atEnd ifTrue: [ ^threeArgumentBlock value: (firstWord isEmpty ifFalse: [firstWord asSymbol]) value: false value: nil ]. secondWord _ self nextWordOfCharacters. ^secondWord = 'class' ifTrue: [ self atEnd ifTrue: [ threeArgumentBlock value: firstWord asSymbol value: false value: secondWord asSymbol ] ifFalse: [ | thirdWord | thirdWord _ self nextWordOfCharacters. threeArgumentBlock value: firstWord asSymbol value: true value: (thirdWord isEmpty ifFalse: [thirdWord asSymbol]) ] ] ifFalse: [ threeArgumentBlock value: firstWord asSymbol value: false value: (secondWord isEmpty ifFalse: [secondWord asSymbol]) ]! ! !PositionableStream methodsFor: 'conditions' stamp: 'JMM 6/23/2000 15:40'! handlePositioningError "Handle a positioning error." ExternalResource positioningErrorSignal signal ! ! !PositionableStream methodsFor: 'control'! lookBack "Look back." "Internal streams can always do this."! ! !PositionableStream methodsFor: 'control'! stopLookingBack "Stop looking back." "Internal streams can always look back."! ! !PositionableStream methodsFor: 'accessing'! collectionSpecies "Answer the logical species of my collection." ^collection species! ! !PositionableStream methodsFor: 'accessing'! contents "Answer all of my readable elements." "The contents of the slots in my collection after readLimit are undefined." ^collection copyFrom: 1 to: readLimit! ! !PositionableStream methodsFor: 'accessing'! newCollection "Answer a new Collection in which to collect my elements." ^collection species new! ! !PositionableStream methodsFor: 'accessing'! next "Answer the next element and reposition myself after it." "This is an optionally primitive method. Primitive failure occurs when my collection is not an Array or String, I'm positioned at the end already, or my position is somehow out of bounds." ^position >= readLimit ifTrue: [ "Modify my collection and retry." self nextPastEnd] ifFalse: [ position _ position + 1. collection at: position]! ! !PositionableStream methodsFor: 'accessing'! next: anInteger "Answer the next anInteger elements." "Collect those elements into a collection of the same species and size as my collection." | elements | elements _ collection species new: anInteger. 1 to: anInteger do: [:index | elements at: index put: self next]. ^elements! ! !PositionableStream methodsFor: 'accessing'! nextAvailable: anInteger "Answer the next anInteger elements, or however many are available, whichever is fewer." ^((self size - self position) < anInteger) ifTrue: [self upToEnd] ifFalse: [self next: anInteger]! ! !PositionableStream methodsFor: 'accessing'! nextAvailableWithoutWordCutting: anInteger "Assume my collection is composed of Characters. Answer the next anInteger elements, or however many are available, whichever is fewer. Don't answer a collection with a partial word at the end; answer fewer characters if necessary." ^((self size - self position) < anInteger) ifTrue: [self upToEnd] ifFalse: [ | charactersToGet characters oldPosition | oldPosition _ self position. characters _ self nextAvailable: anInteger. charactersToGet _ characters size. [charactersToGet = 0 or: [(characters at: charactersToGet) isSeparator]] whileFalse: [charactersToGet _ charactersToGet - 1]. charactersToGet = 0 ifTrue: [charactersToGet _ characters size + 1]. self position: oldPosition; next: charactersToGet - 1]! ! !PositionableStream methodsFor: 'accessing' stamp: 'crl 10/19/1999 20:08'! nextLine "Assuming my elements are characters, answer the next characters up to an end-of-line character." | line | line _ self nextUntil: [:ch | ch isEOL]. self atEnd ifFalse: [self skip: 1]. ^line! ! !PositionableStream methodsFor: 'accessing'! nextSequenceDelimitedBy: separatorBlock "Answer the next sequence of elements in which separatorBlock evaluates to (false) with each element." ^self skipWhile: separatorBlock; nextUntil: separatorBlock! ! !PositionableStream methodsFor: 'accessing'! nextUntil: aBlockClosure "Answer the next contiguous sequence of elements with which aBlockClosure evaluates to false." ^self nextWhile: aBlockClosure is: false! ! !PositionableStream methodsFor: 'accessing'! nextWhile: aBlockClosure "Answer the next contiguous sequence of elements with which aBlockClosure evaluates to true." ^self nextWhile: aBlockClosure is: true! ! !PositionableStream methodsFor: 'accessing' stamp: 'JMM 6/24/2000 01:36'! nextWhile: aBlockClosure is: aBoolean "Answer the next contiguous sequence of elements with which aBlockClosure evaluates to aBoolean." ^[ | stream nextElement | stream _ WriteableStream on: self newCollection. self lookBack. [ self atEnd ifTrue: [^stream contents]. nextElement _ self next. (aBlockClosure value: nextElement) = aBoolean ] whileTrue: [stream nextPut: nextElement]. self skipBack. stream contents ] ensure: [self stopLookingBack]! ! !PositionableStream methodsFor: 'accessing'! nextWordOfCharacters "Answer the next lexical word." ^self nextSequenceDelimitedBy: [:character | character isSeparator]! ! !PositionableStream methodsFor: 'accessing'! peek "Answer the next element without changing position." | nextElement | self atEnd ifTrue: [^nil]. nextElement _ self next. position _ position - 1. ^nextElement! ! !PositionableStream methodsFor: 'accessing'! peek: anInteger "Answer the next anInteger elements without changing position." | elements oldPosition | oldPosition _ self position. elements _ self next: anInteger. self position: oldPosition. ^elements! ! !PositionableStream methodsFor: 'accessing'! peekBack "Answer the previous element without changing position." ^position = 0 ifTrue: [nil] ifFalse: [ self position: position - 1; next]! ! !PositionableStream methodsFor: 'accessing'! peekFor: anObject "Answer whether the next element is equal to anObject. Increment the position if it is." "Open-coded for speed (we could have used >>valueUnlessTrueDoFirst:)." ^self peek = anObject ifTrue: [ "It's faster to increment the position via the primitive >>next method than by normal addition." self next. true] ifFalse: [false]! ! !PositionableStream methodsFor: 'accessing'! readLimit "Answer my read limit." ^readLimit! ! !PositionableStream methodsFor: 'accessing'! size "Answer the number of readable elements I have." ^readLimit! ! !PositionableStream methodsFor: 'accessing'! through: anObject "Answer my next elements up to and including anObject." ^self throughAll: (self collectionSpecies with: anObject)! ! !PositionableStream methodsFor: 'accessing'! throughAll: pattern "Answer my next elements up to and including those in pattern." | elements | elements _ (self collectionSpecies new: 40) writeableStream. elements nextPutAll: (self upToAll: pattern). self atEnd ifFalse: [ elements nextPutAll: ( (position isNumber not and: [position]) ifTrue: [self stopLookingAhead] ifFalse: [self next: (pattern size)])]. ^elements contents! ! !PositionableStream methodsFor: 'accessing'! upTo: anObject "Answer a collection of elements from the current position to the first occurrence of an object equal to anObject. If there is no such element, answer all the elements from the current position to the end." | stream element | stream _ WriteableStream emptyOn: (collection species new: 200). [ self atEnd or: [ element _ self next. element = anObject] ] whileFalse: [stream nextPut: element]. ^stream contents! ! !PositionableStream methodsFor: 'accessing'! upToAll: pattern "Answer a subcollection from the current position to the occurrence (if any, but not inclusive) of pattern in my elements. If pattern is not in the collection, answer the entire rest of my elements." | elements patternSize subCollection initialPosition endingPosition startOfCheck | patternSize _ pattern size. initialPosition _ position. startOfCheck _ position. elements _ (collection species new: 200) emptyWriteableStream. [ "If I am closer to the end than the size of the pattern, that means I didn't find the pattern; return the whole rest of the stream." (self readLimit - position) < patternSize ifTrue: [ self position: initialPosition. ^self upToEnd] ifFalse: [ "If the pattern has been found, answer up to the pattern" subCollection _ self next: patternSize. subCollection = pattern ifTrue: [ endingPosition _ position - patternSize. self position: initialPosition. ^self next: (endingPosition - initialPosition)] ifFalse: [ "Move the stream pointer to one past where we were last time" "If you got an error here, pattern probably isn't a collection (like it's supposed to be)." startOfCheck _ startOfCheck + 1. self position: startOfCheck]] ] repeat. ^elements contents! ! !PositionableStream methodsFor: 'accessing'! upToEnd "Answer my elements from the current position to the end." ^self next: self size - self position! ! !PositionableStream methodsFor: 'positioning'! position "Answer the current position." "See my class comment for the precise meaning of 'position'." ^position! ! !PositionableStream methodsFor: 'positioning'! position: anInteger "Set the current position to anInteger." "See my class comment for the precise meaning of 'position'." (anInteger >= 0 and: [anInteger <= readLimit]) ifTrue: [position _ anInteger] ifFalse: [self handlePositioningError]! ! !PositionableStream methodsFor: 'positioning'! readPosition "Answer my reading position." ^self position! ! !PositionableStream methodsFor: 'positioning'! readPosition: anInteger "Set my reading position to anInteger." self position: anInteger! ! !PositionableStream methodsFor: 'positioning'! reset "Reset my position to the beginning." "Open-coded for speed." position _ 0! ! !PositionableStream methodsFor: 'positioning'! setToEnd "Position myself after the last readable element." position _ readLimit! ! !PositionableStream methodsFor: 'positioning'! skip "Skip forward one position." self skip: 1! ! !PositionableStream methodsFor: 'positioning'! skip: anInteger "Skip anInteger elements." "A subclass might coerce anInteger into my domain." self position: self position + anInteger! ! !PositionableStream methodsFor: 'positioning'! skipBack "Skip back one position." self skip: -1! ! !PositionableStream methodsFor: 'positioning'! skipThrough: anObject "Skip all elements between the current position and the next occurrence of anObject. Position myself so that the next object is the one immediately following anObject. If no occurrences of anObject are found between the current position and the end of the stream, set the position to the end and answer nil" [self atEnd] whileFalse: [self next = anObject ifTrue: [^self]]. ^nil! ! !PositionableStream methodsFor: 'positioning'! skipTo: anObject "Skip to the next element equivalent to anObject. Answer whether such an element is found. If no such element is found, skip to the end." [self atEnd] whileFalse: [self next = anObject ifTrue: [^true]]. ^false! ! !PositionableStream methodsFor: 'positioning'! skipUntil: aBlockClosure "Skip to the next element with which aBlockClosure evaluates to true." self skipWhile: aBlockClosure is: false! ! !PositionableStream methodsFor: 'positioning'! skipWhile: aBlockClosure "Skip to the next element with which aBlockClosure evaluates to false." self skipWhile: aBlockClosure is: true! ! !PositionableStream methodsFor: 'positioning' stamp: 'JMM 6/24/2000 01:36'! skipWhile: aBlockClosure is: aBoolean "Skip to the next element with which aBlockClosure evaluates to aBoolean." [ self lookBack. [ self atEnd ifTrue: [^self]. (aBlockClosure value: self next) = aBoolean ] whileTrue. self skipBack ] ensure: [self stopLookingBack]! ! !PositionableStream methodsFor: 'refreshing' stamp: 'JMM 6/23/2000 15:43'! nextPastEnd "Answer the next element past the end of my collection." ^ExternalResource notEnoughElementsSignal signal ! ! !PositionableStream methodsFor: 'testing'! atEnd "Answer whether I cannot access any more elements." "This is an optionally primitive method. Primitive failure occurs if either position or readLimit is not a SmallInteger." ^position >= readLimit! ! !PositionableStream methodsFor: 'testing'! beginsWith: aCollection "Answer whether my collection begins with aCollection." ^self size < aCollection size ifTrue: [false] ifFalse: [ | startingCollection oldPosition | oldPosition _ self position. startingCollection _ self next: (aCollection size). self position: oldPosition. startingCollection = aCollection]! ! !PositionableStream methodsFor: 'testing'! isEmpty "Answer whether I have no elements available to read." ^readLimit = 0! ! !PositionableStream methodsFor: 'testing'! lookingAhead "Answer whether I'm looking ahead." "Only NetStreams look ahead currently." ^false! ! !PositionableStream methodsFor: 'initialization'! collection: aCollection "Set my collection to aCollection." self emptyCollection: aCollection; initializeReadLimit! ! !PositionableStream methodsFor: 'initialization'! collection: aCollection position: anInteger "Set my collection to aCollection and my position to anInteger." self collection: aCollection; position: anInteger! ! !PositionableStream methodsFor: 'initialization'! collection: aCollection position: anInteger readLimit: anotherInteger "Set my collection to aCollection, my position to anInteger, and my readLimit to anotherInteger." "This method is intended to be invoked *only* by PositionableStream>>on:to:setPositionBefore:." self collection: aCollection position: anInteger. readLimit _ anotherInteger! ! !PositionableStream methodsFor: 'initialization'! emptyCollection: aCollection "Set my collection to aCollection, which is empty as far as readers are concerned." collection _ aCollection. position _ readLimit _ 0! ! !PositionableStream methodsFor: 'initialization'! initializeReadLimit "Initialize my readLimit (for a non-empty collection)." readLimit _ collection size! ! !PositionableStream methodsFor: 'postprocessing'! wrapWithIndentsAt: anInteger "Assuming my collection is populated with Characters, insert line-ends and tabs so that no line is longer than anInteger characters, and newly-created lines begin with a tab." | stream | stream _ (collection species new: collection size) writeableStream. self reset. [self atEnd] whileFalse: [ | nextLine | nextLine _ (self upTo: CR) stream. (nextLine atEnd) ifFalse: [ stream nextPutAll: (nextLine nextAvailableWithoutWordCutting: anInteger). [nextLine atEnd] whileFalse: [ nextLine skip. stream crtab; nextPutAll: (nextLine nextAvailableWithoutWordCutting: ((anInteger - 8) max: 1))]]. stream cr]. ^stream contents! ! !Stream class methodsFor: 'instance creation'! new "Inform the developer that creating instances of myself and my subclasses requires more information." ^self error: 'Streams are created with >>on:, etc.'! ! !PositionableStream class methodsFor: 'backward compatibility-instance creation'! on: aCollection from: firstIndex to: lastIndex "Answer an instance of myself on a copy of the elements from the beginning to lastIndex in aCollection." "This method is considered 'backward compatibility' because its selector doesn't make clear whether the elements before firstIndex will be accessible." ^self on: aCollection to: lastIndex setPositionBefore: firstIndex! ! !PositionableStream class methodsFor: 'instance creation'! emptyOn: aCollection "Answer a new instance of myself on aCollection, which is functionally empty." ^self basicNew emptyCollection: aCollection! ! !PositionableStream class methodsFor: 'instance creation'! fullOn: aCollection "Answer an instance of myself which can read all the current elements of aCollection." ^self on: aCollection to: aCollection size setPositionBefore: 1! ! !PositionableStream class methodsFor: 'instance creation'! on: aCollection "Answer a new instance of myself on aCollection." ^self basicNew collection: aCollection! ! !PositionableStream class methodsFor: 'instance creation'! on: aCollection to: lastElementIndex setPositionBefore: initialNextElementIndex "Answer a new instance of myself on aCollection to lastElementIndex, with an initial position just before initialNextElementIndex." ^(self basicNew) collection: aCollection position: initialNextElementIndex - 1 readLimit: lastElementIndex! ! !String class methodsFor: 'instance creation'! readFrom: inStream "Answer an instance of me that is determined by reading the stream, inStream. Embedded double quotes become the quote Character." | outStream char done | outStream _ WriteableStream on: (String new: 16). "go to first quote" inStream skipTo: $'. done _ false. [done or: [inStream atEnd]] whileFalse: [char _ inStream nextOrNil. char = $' ifTrue: [char _ inStream nextOrNil. char = $' ifTrue: [outStream nextPut: char] ifFalse: [done _ true]] ifFalse: [outStream nextPut: char]]. ^outStream contents! ! !WriteableStream methodsFor: 'character writing'! cr "Write a carriage-return." self nextPut: CR! ! !WriteableStream methodsFor: 'character writing'! crlf "Write a 'carriage-return' and a 'linefeed'." "NOTE: The system line-end convention is CR, so you should only need to use crlf when transfering ascii code out of the system to a platform that uses crlf as it's line end convention." self cr; nextPut: Character lf! ! !WriteableStream methodsFor: 'character writing'! crtab "Write a carriage-return and a tab." self cr; tab! ! !WriteableStream methodsFor: 'character writing'! crtab: anInteger "Write a carriage-return and anInteger tabs." self cr. anInteger timesRepeat: [self tab]! ! !WriteableStream methodsFor: 'character writing'! space "Write a space." self nextPut: Space! ! !WriteableStream methodsFor: 'character writing'! tab "Write a tab." self nextPut: Tab! ! !WriteableStream methodsFor: 'accessing'! basicNextPut: anObject "Put anObject at the next position and answer it." position _ position + 1. ^collection at: position put: anObject! ! !WriteableStream methodsFor: 'accessing'! contents "Answer all of my readable elements." "Recompute my size to account for recently-written elements." self size. ^super contents! ! !WriteableStream methodsFor: 'accessing'! copyFrom: aStream "Copy the remaining elements from aStream." [aStream atEnd] whileFalse: [self nextPut: aStream next]! ! !WriteableStream methodsFor: 'accessing'! nextPut: anObject "Put anObject at the next position and answer it." "This is a optionally primitive method. Primitive failure occurs if my collection is not an Array or String, if I'm positioned at the end, if my position is somehow out of the bounds of my collection, or if anObject is not of a format compatible with that of my collection." ^position = writeLimit ifTrue: [ "Modify my collection and retry." self pastEndPut: anObject] ifFalse: [ position _ position + 1. collection at: position put: anObject]! ! !WriteableStream methodsFor: 'accessing'! nextPutAll: aSequenceableCollection "Put each of the elements of aSequenceableCollection from the next position. Answer aSequenceableCollection." aSequenceableCollection do: [:element | self nextPut: element]. ^aSequenceableCollection! ! !WriteableStream methodsFor: 'accessing'! previousTake "Take back the last written element." ^self previousTake: 1! ! !WriteableStream methodsFor: 'accessing'! previousTake: anInteger "Take back the last anInteger written elements, and answer them (in the order in which they were written)." "Note this implementation is only correct for internal, positionable streams." | elements | readLimit _ position. position _ position - anInteger. elements _ collection copyFrom: position + 1 to: readLimit. readLimit _ position. ^elements! ! !WriteableStream methodsFor: 'accessing'! size "Answer the number of readable elements I have." "Update my readLimit first to account for recently-written elements." ^readLimit _ readLimit max: position! ! !WriteableStream methodsFor: 'printing'! print: anObject "Print anObject on myself." anObject printOn: self! ! !WriteableStream methodsFor: 'printing'! printVerbosely: anObject "Verbosely print anObject on myself." anObject printVerboselyOn: self! ! !WriteableStream methodsFor: 'printing'! store: anObject "Store anObject on myself in such a way that an equivalent object may be read later." anObject storeOn: self! ! !WriteableStream methodsFor: 'positioning'! position: anInteger "Set my position to anInteger." "Update my readLimit first." self size. super position: anInteger! ! !WriteableStream methodsFor: 'positioning'! reset "Reset my position to the beginning." self resetAndBeEmpty! ! !WriteableStream methodsFor: 'positioning'! resetAndBeEmpty "Reset, and have no readable elements." position _ readLimit _ 0! ! !WriteableStream methodsFor: 'positioning'! resetAndRetainContents "Reset my position to the beginning, and retain my contents (normally resetting a writeable stream causes it to be empty)." self size. position _ 0! ! !WriteableStream methodsFor: 'positioning'! retract "Retract the last-written element." self retract: 1! ! !WriteableStream methodsFor: 'positioning'! retract: anInteger "Retract anInteger elements." self skip: anInteger negated; size. readLimit _ readLimit - anInteger! ! !WriteableStream methodsFor: 'positioning'! setToEnd "Position myself after the last readable element." self size. ^super setToEnd! ! !WriteableStream methodsFor: 'positioning'! writePosition "Answer my writing position." ^self position! ! !WriteableStream methodsFor: 'backward compatibility'! applyAttribute: att beginningAt: startPos "Do nothing"! ! !WriteableStream methodsFor: 'backward compatibility'! emphasis: ignored "For compatibility with streams which carry emphasis."! ! !WriteableStream methodsFor: 'backward compatibility'! fileOutChanges "Append to the receiver a description of all class changes." Cursor write showWhile: [self timeStamp. Smalltalk changes fileOutOn: self. self shorten; close]! ! !WriteableStream methodsFor: 'backward compatibility'! nextChunkPut: aString "Append the argument, aString, to the receiver, doubling embedded terminators." | i remainder terminator | terminator _ $!!. remainder _ aString. [(i _ remainder indexOf: terminator) = 0] whileFalse: [self nextPutAll: (remainder copyFrom: 1 to: i). self nextPut: terminator. "double imbedded terminators" remainder _ remainder copyFrom: i+1 to: remainder size]. self nextPutAll: remainder; nextPut: terminator! ! !WriteableStream methodsFor: 'backward compatibility'! nextPutAllText: aText "For compatibility with TextStream." ^self nextPutAll: aText! ! !WriteableStream methodsFor: 'backward compatibility'! setFrom: newStart to: newStop "Set my readLimit to newStop and position myself just before newStart." "This is a backward-compatability method used by ParagraphEditor. It should be removed." position _ newStart - 1. readLimit _ newStop! ! !WriteableStream methodsFor: 'backward compatibility'! timeStamp "Append the current time to the receiver." | aStream | aStream _ WriteableStream on: (String new: 16). Smalltalk timeStamp: aStream. self nextChunkPut: aStream contents printString. "double quotes and !!s" self cr; cr! ! !WriteableStream methodsFor: 'backward compatibility'! withAttribute: att do: strmBlock ^strmBlock value! ! !WriteableStream methodsFor: 'backward compatibility'! writeableStream "Used by various random abusers. This should be removed." ^self! ! !WriteableStream methodsFor: 'testing'! isEmpty "Answer whether I have no elements available to read." self size. ^super isEmpty! ! !WriteableStream methodsFor: 'utilities'! shrink "Use a new collection with no unused slots." self collection: (collection copyTo: position)! ! !WriteableStream methodsFor: 'refreshing'! pastEndPut: anObject "Refresh my collection and write anObject." "If we decide to implement >>grow in any of the collection Classes, we'll probably want to use that message here." collection _ collection, (collection class new: ((collection size min: 100000) max: 8)). writeLimit _ collection size. position _ position + 1. ^collection at: position put: anObject! ! !WriteableStream methodsFor: 'initialization'! emptyCollection: aCollection "Set my collection to aCollection, which has no readable elements." super emptyCollection: aCollection. writeLimit _ collection size! ! !DiscardingStream methodsFor: 'accessing'! next "Answer the next element." ^self next: 1! ! !DiscardingStream methodsFor: 'accessing'! next: anInteger "Answer the next anInteger elements." readLimit _ collection size - anInteger. position _ position - anInteger. ^collection removeFirst: anInteger! ! !DiscardingStream methodsFor: 'accessing'! nextPut: anObject "Put anObject at the next position and answer it." position _ position + 1. ^collection add: anObject! ! !ExternalStream methodsFor: 'nonhomogeneous accessing'! nextByte ^ self next asInteger! ! !ExternalStream methodsFor: 'nonhomogeneous accessing'! nextLittleEndianNumber: n "Answer the next n bytes as a positive Integer or LargePositiveInteger, where the bytes are ordered from least significant to most significant." | s bytes | bytes _ self next: n. s _ 0. n to: 1 by: -1 do: [: i | s _ (s bitShift: 8) bitOr: (bytes at: i)]. ^ s! ! !ExternalStream methodsFor: 'nonhomogeneous accessing'! nextLittleEndianNumber: n put: integer "Put the next n bytes as a positive integer where the bytes are ordered from least significant to most significant." 1 to: n do: [: i | self nextPut: (integer digitAt: i)]! ! !ExternalStream methodsFor: 'nonhomogeneous accessing'! nextWord "Answer the next four bytes from the receiver as an Integer." | value | value _ self next asInteger. value _ (value bitShift: 8) + self next asInteger. value _ (value bitShift: 8) + self next asInteger. value _ (value bitShift: 8) + self next asInteger. ^ value! ! !ExternalStream methodsFor: 'nonhomogeneous accessing'! nextWord16 "For compatibility with old systems only" "Answer the next two bytes from the receiver as an Integer." | high low str | true ifTrue: ["Slower code works while file next fails on non-ascii data" str _ self next: 2. high _ str at: 1. low _ str at: 2] ifFalse: [high _ self next. low _ self next]. ^(high asInteger bitShift: 8) + low asInteger! ! !ExternalStream methodsFor: 'nonhomogeneous accessing'! nextWordPut: aWord "Append to the receiver an Integer as the next four bytes." self nextPut: (Character value: ((aWord bitShift: -24) bitAnd: 255)). self nextPut: (Character value: ((aWord bitShift: -16) bitAnd: 255)). self nextPut: (Character value: ((aWord bitShift: -8) bitAnd: 255)). self nextPut: (Character value: (aWord bitAnd: 255)). ^ aWord! ! !ExternalStream methodsFor: 'initialization'! resource: anExternalResource "Set my resource to anExternalResource." binary _ false. resource _ anExternalResource! ! !ExternalStream methodsFor: 'control'! beBinary "Treat my contents as binary data." binary _ true! ! !ExternalStream methodsFor: 'control'! beCharacters "Treat my contents as characters." binary _ false! ! !ExternalStream methodsFor: 'control'! close "Close my resource." resource isOpen ifTrue: [resource close]! ! !ExternalStream methodsFor: 'testing'! isBinary ^binary! ! !ExternalStream methodsFor: 'testing'! isExternal ^true! ! !ExternalStream methodsFor: 'accessing'! collectionSpecies "Answer the logical species of my collection." ^binary ifTrue: [ByteArray] ifFalse: [String]! ! !ExternalStream methodsFor: 'copying'! postCopy "Finish copying myself." resource _ resource copy! ! !ExternalStream methodsFor: 'backward compatibility'! nextInt32 "Read a 32-bit signed integer from the next 4 bytes" | s | s _ 0. 1 to: 4 do: [:i | s _ (s bitShift: 8) + self next]. (s bitAnd: 16r80000000) = 0 ifTrue: [^ s] ifFalse: [^ -1 - s bitInvert32]! ! !ExternalStream methodsFor: 'backward compatibility'! nextInt32Put: int32 "Write a signed integer to the next 4 bytes" | pos | pos _ int32 < 0 ifTrue: [(0-int32) bitInvert32 + 1] ifFalse: [int32]. 1 to: 4 do: [:i | self nextPut: (pos digitAt: 5-i)]. ^ int32! ! !ExternalStream methodsFor: 'backward compatibility'! nextNumber: n "Answer the next n bytes as a positive Integer or LargePositiveInteger." | s | s _ 0. 1 to: n do: [:i | s _ (s bitShift: 8) bitOr: self next]. ^ s normalize! ! !ExternalStream methodsFor: 'printing' stamp: 'crl 11/3/1999 03:45'! printInactivityExplanationOn: aStream "Print an inactivity explanation on aStream." aStream nextPutAll: ', until a '; nextPutAll: ( resource peerClosed ifTrue: ['remote'] ifFalse: ['local']); nextPutAll: ' disconnection occurred'! ! !ExternalStream methodsFor: 'private' stamp: 'JMM 6/26/2000 15:58'! resource ^resource! ! !NetStream methodsFor: 'control-serial'! baudRate: anInteger "Set the baud rate of my serial port resource to anInteger." resource baudRate: anInteger! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:08'! numberOfDataBits "Set the number of data bits per byte of my serial port resource to anInteger." ^resource numberOfDataBits! ! !NetStream methodsFor: 'control-serial'! numberOfDataBits: anInteger "Set the number of data bits per byte of my serial port resource to anInteger." resource numberOfDataBits: anInteger! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:07'! numberOfStopBits ^resource numberOfStopBits! ! !NetStream methodsFor: 'control-serial'! numberOfStopBits: anInteger "Set the number of stop bits per byte of my serial port resource to anInteger." resource numberOfStopBits: anInteger! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:11'! parityScheme ^resource parityScheme! ! !NetStream methodsFor: 'control-serial'! parityScheme: aSymbol "Set the parity scheme of my serial port resource to aSymbol." resource parityScheme: aSymbol! ! !NetStream methodsFor: 'control-serial'! portNumber: anInteger "Set the port number of my serial port resource to anInteger." resource portNumber: anInteger! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:12'! useCTS ^resource useCTS! ! !NetStream methodsFor: 'control-serial'! useCTS: aBoolean "Set whether my serial port resource should use CTS to aBoolean." resource useCTS: aBoolean! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:12'! useDTR ^resource useDTR! ! !NetStream methodsFor: 'control-serial'! useDTR: aBoolean "Set whether my serial port resource should use DTR to aBoolean." resource useDTR: aBoolean! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:11'! useXOnXOffFlowControl ^resource useXOnXOffFlowControl! ! !NetStream methodsFor: 'control-serial'! useXOnXOffFlowControl: aBoolean "Set whether my serial port resource should use xon-xoff flow control to aBoolean." resource useXOnXOffFlowControl: aBoolean! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:09'! xOffByte ^resource xOffByte! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:09'! xOffByte: aByte resource xOffByte: aByte. ! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:09'! xOnByte ^resource xOnByte! ! !NetStream methodsFor: 'control-serial' stamp: 'JMM 7/4/2000 15:09'! xOnByte: aByte resource xOnByte: aByte. ! ! !NetStream methodsFor: 'control'! lookAhead "Look ahead one position. Answer the discarded contents of the previous lookahead buffer, if any." ^self lookAhead: 1! ! !NetStream methodsFor: 'control' stamp: 'JMM 6/30/2000 13:46'! lookAhead: anInteger "Look ahead anInteger positions. Answer the discarded contents of the previous lookahead buffer, if any." | discarded | position ifTrue: [ | size | size _ collection size. anInteger > size ifTrue: [ collection _ ( (ByteArray new: anInteger) replaceFrom: 1 to: size with: collection; yourself). [ self next: anInteger - size into: collection startingAt: size + 1 timeoutAfter: 0 ] on: ExternalResource failedSocketReadingAttemptSignal, ExternalResource failedUDPSocketReadingAttemptSignal do: [:exception | collection _ collection copyTo: size. exception pass]] ifFalse: [ anInteger < size ifTrue: [ discarded _ collection copyFrom: 1 to: size - anInteger. collection _ collection copyFrom: size - anInteger + 1 to: size]]] ifFalse: [collection _ (self next: anInteger) asByteArray]. position _ true. ^discarded == nil ifTrue: [self collectionSpecies new] ifFalse: [ binary ifTrue: [discarded] ifFalse: [discarded asString]]! ! !NetStream methodsFor: 'control' stamp: 'crl 7/1/2000 01:28'! lookBack "Look back one position." self lookBack: 1! ! !NetStream methodsFor: 'control' stamp: 'crl 7/1/2000 01:28'! lookBack: anInteger "Look back anInteger positions." lookBackStream _ DiscardingStream new: anInteger! ! !NetStream methodsFor: 'control'! reopen "Reopen myself." resource reopen! ! !NetStream methodsFor: 'control'! resetNumberOfBytesRead "Reset the numberOfBytesRead." numberOfBytesRead _ 0! ! !NetStream methodsFor: 'control'! stopLookingAhead "Stop looking ahead. Answer the contents of the lookahead buffer, if any." | elements | elements _ collection. position _ false. ^binary ifTrue: [elements] ifFalse: [elements asString]! ! !NetStream methodsFor: 'control'! stopLookingBack "Stop looking back." lookBackStream _ nil! ! !NetStream methodsFor: 'accessing'! baudRate "Assuming I'm connected to a SerialPort, answer my baudRate." ^resource baudRate! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 6/24/2000 01:36'! beCharactersWhile: aBlockClosure "Be in characters mode while evaluating aBlockClosure." | wasBinary | wasBinary _ binary. self beCharacters. aBlockClosure ensure: [wasBinary ifTrue: [self beBinary]]! ! !NetStream methodsFor: 'accessing'! contents "Answer all of my readable elements." ^self error: 'My complete sequence is undefined.'! ! !NetStream methodsFor: 'accessing'! cr "Write a 'carriage-return'." binary ifFalse: [super cr]! ! !NetStream methodsFor: 'accessing'! crlf "Write a 'carriage-return' and a 'linefeed'." binary ifFalse: [super crlf]! ! !NetStream methodsFor: 'accessing'! newCollection "Answer a new Collection in which to collect my elements." ^binary ifTrue: [ByteArray new] ifFalse: ['']! ! !NetStream methodsFor: 'accessing'! next "Answer the next byte, waiting for it to be available if neccesary." ^(self next: 1) at: 1! ! !NetStream methodsFor: 'accessing'! next: anInteger "Answer the next anInteger bytes, waiting indefinitely for them to be available if necessary." ^self next: anInteger timeoutAfter: -1! ! !NetStream methodsFor: 'accessing'! next: anInteger into: aByteArray "Write the next anInteger bytes from the Net into aByteArray." self next: anInteger into: aByteArray startingAt: 1! ! !NetStream methodsFor: 'accessing'! next: anInteger into: aByteArray startingAt: startIndex "Write the next anInteger bytes from the Net into aByteArray, starting at startIndex." self next: anInteger into: aByteArray startingAt: startIndex timeoutAfter: -1! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 6/27/2000 17:54'! next: anInteger into: aByteArray startingAt: startIndex timeoutAfter: timeoutInMilliseconds "Write the next anInteger bytes from the Net into aByteArray, starting at startIndex. If timeoutInMilliseconds milliseconds pass without forward progress, handle the situation." | numberOfElementsRead | numberOfElementsRead _ 0. [numberOfElementsRead < anInteger] whileTrue: [ | numberOfElementsLastRead | [resource waitForReadabilityTimeoutAfter: timeoutInMilliseconds] on: ExternalResource networkTimeoutSignal do: [:exception | (ExternalResource networkTimeoutSignal bytesRead: numberOfElementsRead) signal]. numberOfElementsLastRead _ ( resource next: anInteger - numberOfElementsRead into: aByteArray startingAt: startIndex + numberOfElementsRead). numberOfElementsLastRead = 0 ifTrue: [ "A TCP socket which is closed by the peer becomes readable... with no data." (ExternalResource readingInterruptedSignal bytesRead: numberOfElementsRead) signal] ifFalse: [ numberOfElementsRead _ numberOfElementsRead + numberOfElementsLastRead. numberOfBytesRead _ numberOfBytesRead + numberOfElementsLastRead]]! ! !NetStream methodsFor: 'accessing'! next: anInteger into: aByteArray timeoutAfter: timeoutInMilliseconds "Write the next anInteger bytes from the Net into aByteArray. If timeoutInMilliseconds milliseconds pass between any subset of them arrives, handle the situation." self next: anInteger into: aByteArray startingAt: 1 timeoutAfter: timeoutInMilliseconds! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 6/29/2000 16:34'! next: anInteger timeoutAfter: timeoutInMilliseconds "Answer the next anInteger bytes, waiting no more than timeoutInMilliseconds milliseconds for any block of them to be available." | bytes | bytes _ ( ( binary ifTrue: [ByteArray] ifFalse: [String] ) new: anInteger). position ifTrue: [ "Answer bytes from the look-ahead buffer." | size startingBytesIndex | size _ collection size. startingBytesIndex _ 1. [startingBytesIndex <= anInteger] whileTrue: [ | lookAheadReplenishmentStartingIndex numberOfElementsReadThisTime | lookAheadReplenishmentStartingIndex _ 1. "Get bytes from the look-ahead buffer." numberOfElementsReadThisTime _ (anInteger - startingBytesIndex + 1) min: size. bytes replaceFrom: startingBytesIndex to: startingBytesIndex + numberOfElementsReadThisTime - 1 with: (collection copyTo: numberOfElementsReadThisTime). startingBytesIndex _ startingBytesIndex + numberOfElementsReadThisTime. numberOfElementsReadThisTime < size ifTrue: [ "Shift unread bytes to the front of the look-ahead buffer." collection replaceFrom: 1 to: size - numberOfElementsReadThisTime with: (collection copyFrom: numberOfElementsReadThisTime + 1). lookAheadReplenishmentStartingIndex _ size - numberOfElementsReadThisTime + 1]. "Replenish the look-ahead buffer. If there's no more network data available, just resize the look-ahead buffer. If the look-ahead buffer is empty after that, turn off look-ahead." resource dataAvailable ifTrue: [ [ self next: numberOfElementsReadThisTime into: collection startingAt: lookAheadReplenishmentStartingIndex timeoutAfter: timeoutInMilliseconds ] on: ExternalResource networkTimeoutSignal, ExternalResource readingInterruptedSignal do: [:exception | | lookAheadBufferSize | lookAheadBufferSize _ lookAheadReplenishmentStartingIndex + exception bytesRead - 1. lookAheadBufferSize > 0 ifTrue: [collection _ collection copyTo: lookAheadBufferSize] ifFalse: [self stopLookingAhead]. startingBytesIndex <= anInteger ifTrue: [exception pass]]] ifFalse: [ numberOfElementsReadThisTime = size ifTrue: [self stopLookingAhead] ifFalse: [collection _ collection copyTo: size - numberOfElementsReadThisTime]]]] ifFalse: [ "Answer bytes directly from the Net." self next: anInteger into: bytes timeoutAfter: timeoutInMilliseconds]. transcribeTraffic ifTrue: [ Transcript cr; nextPutAll: 'incoming: '; print: bytes; endEntry]. lookBackStream == nil ifFalse: [lookBackStream nextPutAll: bytes]. ^bytes! ! !NetStream methodsFor: 'accessing'! nextAvailable "Answer the next available bytes." "I should use infinity here." ^self nextAvailable: nil! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 6/30/2000 11:12'! nextAvailable: anInteger "Answer the next anInteger bytes, or however many are available." | numberOfElementsToRead elements readIndefinitely buffer | readIndefinitely _ anInteger == nil. numberOfElementsToRead _ ( readIndefinitely ifTrue: [1024] ifFalse: [anInteger]). buffer _ ByteArray new: numberOfElementsToRead. elements _ WriteableStream on: (ByteArray new: numberOfElementsToRead). [ [numberOfElementsToRead > 0 and: [resource dataAvailable]] whileTrue: [ | numberOfElementsLastRead | position ifTrue: [ "Consume from the look-ahead buffer." | size | size _ collection size. numberOfElementsLastRead _ size min: numberOfElementsToRead. buffer replaceFrom: 1 to: numberOfElementsLastRead with: collection. size > numberOfElementsLastRead ifTrue: [ "Shift old elements out of the look-ahead buffer." collection replaceFrom: 1 to: collection size - numberOfElementsLastRead with: (collection copyFrom: numberOfElementsLastRead + 1)]. self next: numberOfElementsLastRead into: collection startingAt: (collection size - numberOfElementsLastRead + 1)] ifFalse: [ numberOfElementsLastRead _ ( resource next: numberOfElementsToRead into: buffer startingAt: 1). numberOfBytesRead _ numberOfBytesRead + numberOfElementsLastRead]. elements nextPutAll: (buffer copyTo: numberOfElementsLastRead). readIndefinitely ifFalse: [numberOfElementsToRead _ anInteger - elements position]] ] on: ExternalResource readingInterruptedSignal do: [:exception | "Just stop."]. (position and: [numberOfElementsToRead > elements size]) ifTrue: [ "There wasn't enough network data to satisfy the request at the current look-ahead distance. Use look-ahead elements as necessary, resizing the look-ahead buffer as appropriate. If the look-ahead buffer is exhausted, turn off look-ahead. It is the sender's responsibility to re-establish the previous look-ahead distance, if desired." elements nextPutAll: (collection copyTo: (collection size min: numberOfElementsToRead)). numberOfElementsToRead >= collection size ifTrue: [self stopLookingAhead] ifFalse: [collection _ collection copyFrom: numberOfElementsToRead + 1]]. ^binary ifTrue: [elements contents] ifFalse: [elements contents asString]! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 7/3/2000 22:07'! nextMIDIFrequency "Assuming I'm using MIDI, answer the frequency of the next note-on event." | event frequency | frequency _ 0. [frequency = 0] whileTrue: [ event _ ( ( resource waitForReadabilityTimeoutAfter: 0; nextEvent ) stream). frequency _ ( event isEmpty ifTrue: [0] ifFalse: [ event skipUntil: [:byte | (byte bitShift: -4) = 9]. AbstractSound pitchForMIDIKey: (event skip; next)])]. ^frequency! ! !NetStream methodsFor: 'accessing'! nextPacketInto: aByteArray addressInto: address "Assuming I'm using UDP, write the next packet's bytes into aByteArray, and the address from which they came into address. Answer the number of bytes received." ^self nextPacketInto: aByteArray addressInto: address timeoutAfter: -1! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 7/3/2000 10:33'! nextPacketInto: aByteArray addressInto: address timeoutAfter: timeoutInMilliseconds | count | "Assuming I'm using UDP, write the next packet's bytes into aByteArray, and the address from which they came into address. Answer the number of bytes received. Request that a timeout be handled if timeoutInMilliseconds milliseconds pass before the next packet arrives." count _ resource waitForReadabilityTimeoutAfter: timeoutInMilliseconds; nextPacketInto: aByteArray addressInto: address. numberOfBytesRead _ self numberOfBytesRead + count. ^count! ! !NetStream methodsFor: 'accessing'! nextPut: byte "Put byte at the next position and answer it." self nextPutAll: ( ByteArray with: ( binary ifTrue: [byte] ifFalse: [byte asInteger])). ^byte! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 6/27/2000 22:03'! nextPutAll: aCollection "Put each of the elements of aCollection from the next position. Answer aCollection." | elements numberOfElementsWritten numberOfElementsToWrite | elements _ aCollection. elements class == ByteArray ifFalse: [elements _ elements asByteArray]. numberOfElementsWritten _ 0. numberOfElementsToWrite _ elements size. [numberOfElementsWritten < numberOfElementsToWrite] whileTrue: [ resource waitForWriteability. numberOfElementsWritten _ ( numberOfElementsWritten + ( [ resource nextPut: (numberOfElementsToWrite - numberOfElementsWritten) from: elements startingAt: (numberOfElementsWritten + 1) ] on: ExternalResource failedSocketWritingAttemptSignal do: [:exception | exception pass]))]. transcribeTraffic ifTrue: [ Transcript cr; nextPutAll: 'outgoing: '; print: ( binary ifTrue: [aCollection] ifFalse: [aCollection asString]); endEntry]. ^elements! ! !NetStream methodsFor: 'accessing'! nextPutMultiByte: anInteger "Put anInteger, which is larger than eight bits, from the next position. Answer it." | remainder | remainder _ anInteger. [remainder > 0] whileTrue: [ self nextPut: (remainder bitAnd: 2r11111111). remainder _ remainder bitShift: -8]. ^anInteger! ! !NetStream methodsFor: 'accessing'! nextPutPacket: aByteArray "Send aByteArray via UDP to my resource's cached address. Answer how many bytes were actually sent." ^resource sendPacket: aByteArray! ! !NetStream methodsFor: 'accessing'! nextPutPacket: aByteArray toAddress: anInternetSocketAddress "Assuming I'm using UDP, write aByteArray, as a ByteArray, to anInternetSocketAddress." | bytes size index | bytes _ aByteArray asByteArray. size _ aByteArray size. index _ 1. [index <= size] whileTrue: [ index _ ( index + ( resource sendPacket: (bytes copyFrom: index to: size) toAddress: anInternetSocketAddress))]. ^aByteArray! ! !NetStream methodsFor: 'accessing'! nextPutPacket: aByteArray toPort: port atHostNamed: hostname "Assuming I'm using UDP, write aByteArray to port at hostname." ^self nextPutPacket: aByteArray toAddress: (self addressForPort: port atHostNamed: hostname)! ! !NetStream methodsFor: 'accessing'! nextPutString: aString "Write aString to my stream, switching temporarilyto non-binary mode if necessary." self beCharactersWhile: [self nextPutAll: aString]! ! !NetStream methodsFor: 'accessing'! numberOfBytesRead "Answer the numberOfBytesRead in the current reading operation." "No synchronization is provided, in the interest of performance. The answer is a lower bound." ^numberOfBytesRead! ! !NetStream methodsFor: 'accessing'! peek "Answer the next element without consuming it." ^(position and: [collection size > 0]) ifTrue: [ binary ifTrue: [collection first] ifFalse: [collection first asCharacter]] ifFalse: [self lookAhead; peek]! ! !NetStream methodsFor: 'accessing'! peek: anInteger "Answer the next anInteger elements without consuming them." ^position ifTrue: [ anInteger > collection size ifTrue: [self lookAhead: anInteger; peek: anInteger] ifFalse: [ binary ifTrue: [collection copyTo: anInteger] ifFalse: [(collection copyTo: anInteger) asString]]] ifFalse: [self lookAhead: anInteger; peek: anInteger]! ! !NetStream methodsFor: 'accessing'! peerAddress "Assuming I'm connected to a client stream socket, answer its peer address." ^resource peerAddress! ! !NetStream methodsFor: 'accessing'! port "Assuming I'm connected to an IP socket, answer the port to which the socket is listening, or the remote port to which it is connected." ^resource port! ! !NetStream methodsFor: 'accessing'! portNumber "Answer the port number of my serial port resource." resource portNumber! ! !NetStream methodsFor: 'accessing'! readLimit "Answer my read limit." "Should answer positive infinity" ^SmallInteger maxVal! ! !NetStream methodsFor: 'accessing'! stopTranscribingTraffic "Stop transcribing traffic onto the Transcript." transcribeTraffic _ false! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 6/26/2000 16:58'! timedOut ^resource timedOut! ! !NetStream methodsFor: 'accessing'! transcribeTraffic "Transcribe traffic onto the Transcript." transcribeTraffic _ true! ! !NetStream methodsFor: 'accessing'! upTo: anObject "Answer a collection of elements from the current position to the first occurrence of an object equal to anObject." ^self upToAll: (self collectionSpecies with: anObject)! ! !NetStream methodsFor: 'accessing' stamp: 'JMM 6/24/2000 01:36'! upToAll: pattern "Answer the next elements up to but not including those in pattern." | elements elementPattern oldTranscriptionValue | elements _ (String new: 128) writeableStream. elementPattern _ ( binary ifTrue: [pattern] ifFalse: [pattern asByteArray]). oldTranscriptionValue _ transcribeTraffic. ^[ transcribeTraffic ifTrue: [transcribeTraffic _ false]. elements nextPutAll: (self lookAhead: pattern size). [collection = elementPattern] whileFalse: [elements nextPut: self next]. elements _ elements contents. oldTranscriptionValue ifTrue: [Transcript nextPutAll: 'incoming: '; print: elements]. elements ] ensure: [transcribeTraffic _ oldTranscriptionValue]! ! !NetStream methodsFor: 'control-IP' stamp: 'crl 11/7/1999 16:23'! client "Answer a stream to the next client requesting service." ^resource accept! ! !NetStream methodsFor: 'control-IP'! connectToAddress: anInternetSocketAddress "Connect my resource to anInternetSocketAddress." self throttle. resource connectToAddress: anInternetSocketAddress! ! !NetStream methodsFor: 'control-IP'! connectToPort: port atHostNamed: hostname "Connect my resource to port at the host named hostname." self connectToAddress: (self addressForPort: port atHostNamed: hostname)! ! !NetStream methodsFor: 'control-IP'! serveAtPort: port queueSize: queueSize "Set my resource to listen at port, with queueSize." self throttle. resource listenAtPort: port queueSize: queueSize! ! !NetStream methodsFor: 'control-IP' stamp: 'crl 10/20/1999 08:16'! throttle "If my resource is open and connection-oriented, close it and initialize it so that I can do something else with it." (resource usesTCP and: [resource isOpen]) ifTrue: [resource close]. resource initialize! ! !NetStream methodsFor: 'testing' stamp: 'crl 10/19/1999 20:18'! atEnd "Answer whether I can't access any more elements." ^self dataAvailable not! ! !NetStream methodsFor: 'testing'! dataAvailable "Answer whether there is data available for reading." ^resource dataAvailable! ! !NetStream methodsFor: 'testing'! isActive "Answer whether I'm active." ^resource isActive! ! !NetStream methodsFor: 'testing'! isOpen "Answer whether I'm open." ^resource isOpen! ! !NetStream methodsFor: 'testing'! peerClosed "Answer whether my peer closed its side of the connection." ^resource peerClosed! ! !NetStream methodsFor: 'printing'! printOn: aStream "Print a character-based description of myself on aStream." aStream nextPutAll: 'a '. binary ifFalse: [aStream nextPutAll: 'non-']. aStream nextPutAll: 'binary '; print: self class. position ifTrue: [ | lookAheadAmount active | lookAheadAmount _ collection size. active _ self isActive. aStream nextPutAll: ', which '; nextPutAll: ( active ifTrue: ['is'] ifFalse: ['was']); nextPutAll: ' looking ahead '; print: lookAheadAmount; nextPutAll: ' element'. lookAheadAmount > 1 ifTrue: [aStream nextPut: $s]. active ifTrue: [ aStream nextPutAll: ' (and seeing '. binary ifTrue: [aStream print: collection] ifFalse: [ aStream nextPut: $'; nextPutAll: collection asString; nextPut: $']. aStream nextPutAll: '),']]. aStream nextPutAll: ' on '; print: resource. lookBackStream == nil ifFalse: [ aStream nextPutAll: ', which has seen '. binary ifTrue: [aStream print: lookBackStream contents] ifFalse: [ aStream nextPut: $"; nextPutAll: (String newFrom: lookBackStream contents); nextPut: $"]]! ! !NetStream methodsFor: 'positioning'! position "Answer the current position." "See my class comment for the precise meaning of 'position'." ^self error: 'I don''t have a meaningful position.'! ! !NetStream methodsFor: 'positioning'! position: anInteger "Set the current position to anInteger." "See my class comment for the precise meaning of 'position'." ^self error: 'I don''t have a meaningful position.'! ! !NetStream methodsFor: 'positioning'! reset "Ain't no lookin' back now, baby." self error: 'I have no meaningful position (although I can tell you how many bytes I have read, via >>numberOfBytesRead).'! ! !NetStream methodsFor: 'positioning'! skip "Skip one element." self skip: 1! ! !NetStream methodsFor: 'positioning'! skip: anInteger "Skip the next anInteger elements." anInteger < 0 ifTrue: [ "Put -anInteger elements from the lookBackStream onto the look-ahead buffer." | numberOfElementsToMove | numberOfElementsToMove _ anInteger abs. lookBackStream contents size < numberOfElementsToMove ifTrue: [self error: 'I wasn''t looking back that far.'] ifFalse: [ collection _ ( (ByteArray new: collection size + numberOfElementsToMove) replaceFrom: numberOfElementsToMove + 1 to: collection size + numberOfElementsToMove with: collection; replaceFrom: 1 to: numberOfElementsToMove with: (lookBackStream next: numberOfElementsToMove) asByteArray)]] ifFalse: [self next: anInteger]! ! !NetStream methodsFor: 'positioning'! skipSeparators "Assuming I'm non-binary, skip separator characters." [self peek isSeparator] whileTrue: [self skip]! ! !NetStream methodsFor: 'utilities'! addressForPort: port atHostNamed: hostname "Answer the InternetSocketAddress for port at the host named hostname." ^self class addressForPort: port atHostNamed: hostname! ! !NetStream methodsFor: 'initialization'! initialize "Initialize myself." transcribeTraffic _ false. collection _ ByteArray new. "position is used for indicating whether or not look-ahead is taking place." position _ false. numberOfBytesRead _ 0! ! !WriteableStream class methodsFor: 'instance creation'! atTheEndOn: aCollection "Answer an instance of myself on aCollection, positioned to write at the end." ^(super on: aCollection) setToEnd! ! !WriteableStream class methodsFor: 'instance creation'! on: aCollection "Answer a new instance of myself on aCollection." ^self emptyOn: aCollection! ! !WriteableStream class methodsFor: 'instance creation'! onLongString "Answer a new instance of myself on a long String." ^self on: (String new: 64)! ! !WriteableStream class methodsFor: 'instance creation'! onMediumString "Answer a new instance of myself on a medium String." ^self on: (String new: 32)! ! !WriteableStream class methodsFor: 'instance creation'! onShortString "Answer a new instance of myself on a short String." ^self on: (String new: 16)! ! !WriteableStream class methodsFor: 'backward compatibility-instance creation'! with: aCollection "Answer an instance of myself on aCollection, positioned to write at the end." "This method is marked 'backward compatability' because its selector doesn't make clear the fact that writing will initially take place at the end." ^self atTheEndOn: aCollection! ! !DiscardingStream class methodsFor: 'instance creation'! new: anInteger "Answer a new initialize instance of myself with capacity anInteger." ^self on: (OrderedCollection new: anInteger)! ! !ExternalStream class methodsFor: 'instance creation'! on: anExternalResource "Answer an instance of myself on anExternalResource." ^self basicNew resource: anExternalResource! ! !NetStream class methodsFor: 'instance creation'! on: anExternalResource "Answer an instance of myself on anExternalResource." ^(super on: anExternalResource) initialize! ! !NetStream class methodsFor: 'instance creation' stamp: 'crl 3/12/1999 03:12'! onMIDIPortAt: midiPortNumber "Answer an instance of myself on the MIDI port indicated by midiPortNumber." ^self on: (MIDIPort at: midiPortNumber)! ! !NetStream class methodsFor: 'instance creation'! onSerialPortAt: serialPortNumber "Answer an instance of myself on the serial port indicated by serialPortNumber." ^self on: (SerialPort at: serialPortNumber)! ! !NetStream class methodsFor: 'instance creation'! tcpClientToAddress: anInternetSocketAddress "Answer a stream on a TCP client socket to anInternetSocketAddress." ^self on: (OutgoingClientTCPSocket clientToAddress: anInternetSocketAddress)! ! !NetStream class methodsFor: 'instance creation'! tcpClientToPort: port atHostNamed: hostname "Answer a stream on a TCP client socket to port at the host named hostname." ^self tcpClientToAddress: (self addressForPort: port atHostNamed: hostname)! ! !NetStream class methodsFor: 'instance creation'! tcpServerAtPort: port "Answer a stream on a TCP server at port with queueSize." ^self tcpServerAtPort: port queueSize: 1! ! !NetStream class methodsFor: 'instance creation'! tcpServerAtPort: port queueSize: queueSize "Answer a stream on a TCP server at port qith queueSize." ^self on: (TCPSocket serverAtPort: port queueSize: queueSize)! ! !NetStream class methodsFor: 'instance creation'! udpClientToAddress: anInternetSocketAddress "Answer a stream on a UDP client socket to anInternetSocketAddress." ^self on: (UDPSocket clientToAddress: anInternetSocketAddress)! ! !NetStream class methodsFor: 'instance creation'! udpClientToPort: port atHostNamed: hostname "Answer a stream on a UDP client socket to port at the host named hostname." ^self udpClientToAddress: (self addressForPort: port atHostNamed: hostname)! ! !NetStream class methodsFor: 'instance creation'! udpServerAtPort: port "Answer a stream on a UDP server at port with queueSize." "UDP doesn't support queueing." ^self on: (UDPSocket serverAtPort: port)! ! !NetStream class methodsFor: 'instance creation'! udpServerAtPort: port queueSize: queueSize "Answer a stream on a UDP server at port with queueSize." "UDP doesn't support queueing." ^self udpServerAtPort: port! ! !NetStream class methodsFor: 'utilities'! addressForPort: port atHostNamed: hostname "Answer the address for port at the host named hostname." ^InternetSocketAddressResolver addressForPort: port atHostNamed: hostname! ! NetStream class removeSelector: #addSituations! PositionableStream class removeSelector: #addSituations!