"
COPYRIGHT (c) 2004 by eXept Software AG
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
"{ Package: 'stx:libbasic' }"
PeekableStream subclass:#EncodedStream
instanceVariableNames:'encoder stream'
classVariableNames:''
poolDictionaries:''
category:'Collections-Text-Encodings'
!
!EncodedStream class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 2004 by eXept Software AG
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
!
documentation
"
a stream which transparently decodes from an external decoding,
looking for '---- Encoding: xxx' near the beginning of the file.
especially targeted towards reading ST/X source files.
"
! !
!EncodedStream class methodsFor:'instance creation'!
stream:streamArg encoder:encoder
^ (self basicNew) stream:streamArg; encoder:encoder
! !
!EncodedStream class methodsFor:'utilities'!
decodedStreamFor:aStream
"given a positionable stream, guess its encoding (by reading the
first few lines, looking for a string with an encoding hint,
and return an appropriate encoded string, which does the decoding
on the fly. Used mostly to read UTF8 files (source code)"
|encodingSymbol decoder decodedStream|
aStream isPositionable ifTrue:[
encodingSymbol := CharacterEncoder guessEncodingOfStream:aStream.
decoder := CharacterEncoder encoderFor:encodingSymbol.
decoder isNil ifTrue:[
"/ ascii or iso8895-1
^ aStream
].
] ifFalse:[
"/ setup for no-encoding;
"/ switch to a real encoder later,
"/ whenever an encoding pragma is encountered later.
decoder := CharacterEncoder nullEncoderInstance.
].
decodedStream := self stream:aStream encoder:decoder.
"JV@2012-03-27: NO, DO NOT CHANGE POSITION!! Caller might be interested
in all data!!!!!!"
"/decodedStream skipEncodingChunk.
^ decodedStream
"Modified: / 27-03-2013 / 17:08:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
!
!EncodedStream methodsFor:'accessing'!
contentsSpecies
"Not sure if this is ok"
^ stream contentsSpecies.
"/ ^UnicodeString
"Created: / 14-06-2005 / 17:11:01 / janfrog"
!
encoder
^ encoder
!
encoder:something
encoder := something.
!
inputStream
^ stream inputStream
!
lineNumber
^ stream lineNumber
!
pathName
"if our base stream hat a pathname, delegate..."
stream isNil ifTrue:[
^ nil.
].
^ stream pathName.
!
readStream
^ self
!
stream
^ stream
!
stream:something
stream := something.
! !
!EncodedStream methodsFor:'private fileIn'!
basicFileInNotifying:someone passChunk:passChunk
"central method to file in from the receiver, i.e. read chunks and evaluate them -
return the value of the last chunk.
Someone (which is usually some codeView) is notified of errors."
|lastValue pkg nameSpace usedNameSpaces
packageQuerySignal nameSpaceQuerySignal usedNameSpaceQuerySignal
changeDefaultApplicationNotificationSignal
defaultApplicationQuerySignal defaultApplication
confirmationQuerySignal handledSignals passedSignals askSomeoneForPackage outerContext askForVariableTypeOfUndeclaredQuery|
self skipSeparators.
lastValue := self peek.
lastValue == $< ifTrue:[
"/ assume, it's an xml file
^ self fileInXMLNotifying:someone passChunk:passChunk.
].
lastValue == $# ifTrue:[
"assume unix interpreter name:
'#!!stx -e' or something like this"
self nextPeek == $!! ifTrue:[
"skip the unix command line"
self nextLine
] ifFalse:[
self error:'Invalid chunk start'
]
].
Smalltalk::Compiler isNil ifTrue:[
self isFileStream ifTrue:[
Transcript show:('[' , self pathName , '] ').
].
Transcript showCR:'cannot fileIn (no compiler).'.
^ nil.
].
"/ support for V'Age applications
defaultApplicationQuerySignal := Class defaultApplicationQuerySignal.
changeDefaultApplicationNotificationSignal := Class changeDefaultApplicationNotificationSignal.
"/ support for ST/X's nameSpaces & packages
packageQuerySignal := Class packageQuerySignal.
nameSpaceQuerySignal := Class nameSpaceQuerySignal.
usedNameSpaceQuerySignal := Class usedNameSpaceQuerySignal.
(someone respondsTo:#packageToInstall) ifTrue:[
pkg := someone packageToInstall.
askSomeoneForPackage := true.
] ifFalse:[
pkg := packageQuerySignal query.
askSomeoneForPackage := false.
].
(someone respondsTo:#currentNameSpace) ifTrue:[
nameSpace := someone currentNameSpace
] ifFalse:[
nameSpace := nameSpaceQuerySignal query.
].
(someone respondsTo:#usedNameSpaces) ifTrue:[
usedNameSpaces := someone usedNameSpaces
] ifFalse:[
usedNameSpaces := usedNameSpaceQuerySignal query.
].
(someone respondsTo:#defaultApplication) ifTrue:[
defaultApplication := someone defaultApplication
] ifFalse:[
defaultApplication := defaultApplicationQuerySignal query.
].
confirmationQuerySignal := Metaclass confirmationQuerySignal.
handledSignals := SignalSet new.
passedSignals := IdentitySet new.
handledSignals add:changeDefaultApplicationNotificationSignal.
passedSignals add:changeDefaultApplicationNotificationSignal.
handledSignals add:defaultApplicationQuerySignal.
passedSignals add:defaultApplicationQuerySignal.
handledSignals add:packageQuerySignal.
handledSignals add:usedNameSpaceQuerySignal.
handledSignals add:nameSpaceQuerySignal.
handledSignals add:confirmationQuerySignal.
passedSignals add:confirmationQuerySignal.
Parser notNil ifTrue:[
"only if libcomp is present"
"Also catch a 'Parser askForVariableTypeOfUndeclaredQuery' and proceed with nil.
Imagine somebody has autodefine workspace variables on and then
evaluate Smalltalk loadPackage:'xyz' that loads code from source (using file-in),
certainly we don't want to compile workspace variable access for every
not-yet-loaded class in some namespace.
This is demonstrated by Regression::CompilerTests2>>test_01
and this change actually fixes this test."
askForVariableTypeOfUndeclaredQuery := Parser askForVariableTypeOfUndeclaredQuery.
handledSignals add:askForVariableTypeOfUndeclaredQuery.
].
outerContext := thisContext.
handledSignals handle:[:ex |
|sig|
sig := ex creator.
((passedSignals includes:sig) and:[sig isHandledIn:outerContext]) ifTrue:[
ex reject
].
sig == changeDefaultApplicationNotificationSignal ifTrue:[
"/ invoked via #becomeDefault to set the defaultApp and the package.
"/ (only when filing in V'Age code)
defaultApplication := ex parameter.
pkg := defaultApplication name asSymbol.
ex proceedWith:nil
].
sig == defaultApplicationQuerySignal ifTrue:[
"/ query for the application to add classes & methods into
"/ (only when filing in V'Age code)
ex proceedWith:defaultApplication
].
sig == packageQuerySignal ifTrue:[
"answer the package to use for classes & methods"
askSomeoneForPackage ifTrue:[
ex proceedWith:someone packageToInstall
] ifFalse:[
ex proceedWith:pkg
]
].
sig == usedNameSpaceQuerySignal ifTrue:[
"answer the nameSpaces to be searched when encountering globals"
ex proceedWith:usedNameSpaces
].
sig == nameSpaceQuerySignal ifTrue:[
"answer the nameSpace to install new classes in"
ex proceedWith:nameSpace
].
sig == confirmationQuerySignal ifTrue:[
"don't pop up dialogs"
ex proceedWith:false
].
sig == askForVariableTypeOfUndeclaredQuery ifTrue:[
"no autodefined variables or so"
ex proceedWith:nil.
].
] do:[
[self atEnd] whileFalse:[
lastValue := self fileInNextChunkNotifying:someone passChunk:passChunk
]
].
^ lastValue
"Modified: / 10.9.1999 / 16:54:01 / stefan"
"Modified: / 16.11.2001 / 16:21:28 / cg"
"Modified: / 18-03-2013 / 17:45:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !
!EncodedStream methodsFor:'stream protocol'!
atEnd
^ stream atEnd
!
close
stream close
!
contents
^String streamContents:
[:s|
[ stream atEnd ] whileFalse:
[s nextPut: stream next]
]
"Created: / 25-02-2010 / 23:34:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
cr
self nextPutAll:(Character cr asString)
!
emphasis:anObject
stream emphasis:anObject
"Created: / 15-06-2005 / 11:16:33 / janfrog"
!
flush
stream flush
!
isOpen
^ stream notNil and:[stream isOpen]
!
next
^encoder readNextCharacterFrom:stream
"Created: / 14-06-2005 / 17:01:39 / janfrog"
!
next:charactersToRead
^encoder readNext:charactersToRead charactersFrom:stream
"Created: / 16-06-2005 / 11:43:43 / masca"
!
nextChunk
|chunk|
chunk := stream nextChunk.
chunk isNil ifTrue:[^ chunk].
^ encoder decodeString:chunk
!
nextChunkPut:chunk
stream nextChunkPut:(encoder encodeString:chunk)
!
nextPut:aCharacter
self nextPutAll:(aCharacter asString).
!
nextPutAll:aCollection
stream nextPutAll:(encoder encodeString:aCollection).
!
peek
^stream peek
"Created: / 20-06-2005 / 10:13:03 / masca"
"Modified: / 20-06-2005 / 13:06:14 / masca"
!
peekFor:aCharacter
^ stream peekFor:aCharacter
!
position
^ stream position
!
position0Based
<resource: #obsolete>
"to be obsoleted - use position"
^ stream position
!
position0Based:newPosition
<resource: #obsolete>
"to be obsoleted - use position"
stream position:newPosition
!
position1Based
<resource: #obsolete>
"to be obsoleted - use position"
^ stream position + 1
!
position1Based:newPosition
<resource: #obsolete>
"to be obsoleted - use position"
stream position:newPosition-1
!
position:newPosition
stream position:newPosition
!
reset
stream reset
"Created: / 25-02-2010 / 23:37:14 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!
setToEnd
stream setToEnd
!
size
"not correct, but probably better than 0"
"/ is that better?
"/ self error:'size of input is unknown (due to decoding)'
^ stream size
"Created: / 31-08-2012 / 16:52:40 / cg"
!
skip: anInteger
"/ Should skip on character basis, not on bytes. This works for XML reader
^stream skip: anInteger
"Created: / 20-06-2005 / 13:06:06 / masca"
!
skipSeparators
^ stream skipSeparators
!
sync
stream sync
!
syncData
stream syncData
! !
!EncodedStream methodsFor:'testing'!
isReadable
^ stream isReadable
!
isWritable
^ stream isWritable
! !
!EncodedStream methodsFor:'utilities'!
skipEncodingChunk
|pos chunk token|
stream isPositionable ifFalse:[
^ self
].
pos := self position.
chunk := self nextChunk.
[
"/ if this is a valid chunk (i.e. not a comment or encoding-directive),
"/ then position back, so it will be processed as usual.
"/ We could parse here, but this is overkill, since we are only interested in the fact,
"/ that there is anything else than a comment in the chunk.
"/ result := (Parser for:chunk)
"/ ignoreErrors:true;
"/ ignoreWarnings:true;
"/ parseMethodBody.
token := (Scanner for:chunk)
ignoreErrors:true;
ignoreWarnings:true;
nextToken.
token ~= #EOF ifTrue:[
self position:pos
].
] on:Parser parseWarningSignal do:[:ex|
"really ignore any error.
Even setting ignorError will output diagnostics here
during standalone startup when debugging"
ex proceedWith:#ignore.
].
"Modified: / 29-07-2011 / 17:42:11 / cg"
! !
!EncodedStream class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libbasic/EncodedStream.st,v 1.33 2013-08-10 11:31:31 stefan Exp $'
!
version_CVS
^ '$Header: /cvs/stx/stx/libbasic/EncodedStream.st,v 1.33 2013-08-10 11:31:31 stefan Exp $'
!
version_HG
^ '$Changeset: <not expanded> $'
!
version_SVN
^ '$ Id: EncodedStream.st 10643 2011-06-08 21:53:07Z vranyj1 $'
! !