initial checkin
authorStefan Vogel <sv@exept.de>
Thu, 17 Jan 2002 15:25:48 +0100
changeset 1005 7ed6fa7ccfba
parent 1004 c31a7edf9934
child 1006 a459892742f1
initial checkin
FileURI.st
FtpURI.st
HierarchicalURI.st
HttpURI.st
URI.st
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FileURI.st	Thu Jan 17 15:25:48 2002 +0100
@@ -0,0 +1,116 @@
+"{ Package: 'stx:libbasic2' }"
+
+HierarchicalURI subclass:#FileURI
+	instanceVariableNames:''
+	classVariableNames:''
+	poolDictionaries:''
+	category:'Resources'
+!
+
+
+!FileURI class methodsFor:'instance creation'!
+
+fromFilename:aFilename
+    "create an URI based on an a filename"
+
+    ^ self new fromFilename:aFilename
+! !
+
+!FileURI class methodsFor:'accessing'!
+
+schemes
+    "answer the list of supported schemes"
+
+    ^ #(file)
+! !
+
+!FileURI methodsFor:'converting'!
+
+asFilename
+    "answer the receiver represented as filename"
+
+    ^ authority notNil ifTrue:[
+        Filename remoteHost:authority rootComponents:pathSegments.
+    ] ifFalse:[
+        Filename rootComponents:pathSegments
+    ].
+! !
+
+!FileURI methodsFor:'initialize'!
+
+fromFilename:aFilename
+    "create an URI based on an a filename"
+
+    |components|
+
+    components := aFilename components.
+    aFilename isAbsolute ifTrue:[
+        (components size > 3 and:[(components at:2) size == 0]) ifTrue:[
+            "this is a MS-Windows network path: \\host\path"
+            authority := components at:3.
+            pathSegments := components copyFrom:4.
+        ] ifFalse:[
+            "this is an absolute path"
+            isAbsolute := true.
+            pathSegments := components copyFrom:2.
+        ].
+    ] ifFalse:[
+        "this is a relative path"
+        isAbsolute := false.
+        pathSegments := components.
+    ]
+
+    "
+      self fromFilename:'/a/b/c' asFilename
+      self fromFilename:'//a/b/c' asFilename
+      self fromFilename:'a/b/c' asFilename
+    "
+! !
+
+!FileURI methodsFor:'stream access'!
+
+readStream
+
+    ^ self asFilename readStream
+
+    "
+     'file:/etc/group' asURI readStream contents
+    "
+!
+
+readStreamDo:aBlock
+    "evaluate a block with the read stream as first argument
+     and a dictionary containing attributes as second argument.
+     The stream is closed after aBlock has been evaluated."
+
+    |stream|
+
+    ^ [
+        |stream attributes|
+
+        stream := self asFilename readStream.
+        attributes := Dictionary new at:#MIME put:'text/plain'.
+        aBlock value:stream value:attributes
+     ] ensure:[
+        stream notNil ifTrue:[stream close]
+     ].
+
+    "
+     'file:/etc/group' asURI readStreamDo:[:stream :attributes|
+        stream contents         
+                addFirst:attributes printString; 
+                yourself
+     ].
+    "
+!
+
+writeStream
+
+    ^ self asFilename writeStream
+! !
+
+!FileURI class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libbasic2/FileURI.st,v 1.1 2002-01-17 14:24:59 stefan Exp $'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FtpURI.st	Thu Jan 17 15:25:48 2002 +0100
@@ -0,0 +1,70 @@
+"{ Package: 'stx:libbasic2' }"
+
+HierarchicalURI subclass:#FtpURI
+	instanceVariableNames:''
+	classVariableNames:''
+	poolDictionaries:''
+	category:'Resources'
+!
+
+
+!FtpURI class methodsFor:'accessing'!
+
+schemes
+
+    ^ #(ftp)
+! !
+
+!FtpURI methodsFor:'defaults'!
+
+defaultPassword
+
+    ^ 'secret@secret'
+!
+
+defaultPort
+    "answer the default command-port here"
+
+    ^ 21
+!
+
+defaultUser
+
+    ^ 'anonymous'
+! !
+
+!FtpURI methodsFor:'stream access'!
+
+readStreamDo:aBlock
+    "use FTPClient for now"
+
+    |ftp stream|
+
+    ftp := FTPClient new.
+    [
+        ftp connectTo:self host 
+            port:self port 
+            user:(self user ? self defaultUser)
+            password:(self password ? self defaultPassword).
+        stream := ftp getStreamFor:self path.
+        aBlock value:stream value:(Dictionary new at:#MIME put:'text/plain').
+    ] ensure:[
+        stream notNil ifTrue:[
+            stream close.
+        ].
+        ftp notNil ifTrue:[
+            ftp close.
+        ].
+    ]
+    "
+     'ftp://stefan:mschrat.14@hippo/etc/group' asURI readStreamDo:[:stream :attributes | 
+         self halt
+      ].
+    "
+! !
+
+!FtpURI class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libbasic2/FtpURI.st,v 1.1 2002-01-17 14:25:12 stefan Exp $'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HierarchicalURI.st	Thu Jan 17 15:25:48 2002 +0100
@@ -0,0 +1,405 @@
+"{ Package: 'stx:libbasic2' }"
+
+URI subclass:#HierarchicalURI
+	instanceVariableNames:'authority isAbsolute pathSegments query fragment'
+	classVariableNames:''
+	poolDictionaries:''
+	category:'Resources'
+!
+
+!HierarchicalURI class methodsFor:'documentation'!
+
+documentation
+"
+    Hierarchical URI as defined in RFC2396:
+
+    <scheme:[//authority][/absolute_path][?query][#fragment]>
+
+    [author:]
+        Stefan Vogel (stefan@zwerg)
+
+    [see also:]
+
+    [instance variables:]
+
+    [class variables:]
+"
+!
+
+examples
+"
+
+  more examples to be added:
+                                                                [exBegin]
+    ... add code fragment for 
+    ... executable example here ...
+                                                                [exEnd]
+"
+! !
+
+!HierarchicalURI class methodsFor:'instance creation'!
+
+new
+
+    ^ self basicNew initialize
+!
+
+scheme:aSchemeString fromString:aString
+    "parse the hierarchical information.
+     schemeString is ignored here"
+
+    ^ self new scheme:aSchemeString; 
+               fromString:aString
+! !
+
+!HierarchicalURI methodsFor:'accessing'!
+
+authority
+    "return the value of the instance variable 'authority' (automatically generated)"
+
+    ^ authority
+!
+
+authority:something
+    "set the value of the instance variable 'authority' (automatically generated)"
+
+    authority := something.
+!
+
+fragment
+    "return the value of the instance variable 'fragment' (automatically generated)"
+
+    ^ fragment
+!
+
+fragment:something
+    "set the value of the instance variable 'fragment' (automatically generated)"
+
+    fragment := something.
+!
+
+isAbsolute
+    "return the value of the instance variable 'isAbsolute' (automatically generated)"
+
+    ^ isAbsolute
+!
+
+pathSegements
+    "return the value of the instance variable 'pathSegements' (automatically generated)"
+
+    ^ pathSegments
+!
+
+pathSegements:something
+    "set the value of the instance variable 'pathSegements' (automatically generated)"
+
+    pathSegments := something.
+!
+
+query
+    "return the value of the instance variable 'query' (automatically generated)"
+
+    ^ query
+!
+
+query:something
+    "set the value of the instance variable 'query' (automatically generated)"
+
+    query := something.
+! !
+
+!HierarchicalURI methodsFor:'accessing-details'!
+
+host
+    "answer the host part of authority"
+
+    |start end|
+
+    authority isNil ifTrue:[
+        ^ nil
+    ].
+    start := (authority indexOf:$@) + 1.
+    end := (authority indexOf:$: startingAt:start) - 1.
+    end == -1 ifTrue:[
+        end := authority size
+    ].
+    ^ authority copyFrom:start to:end
+
+    "
+      (self fromString:'ftp://stefan@www.exept.de:80/test') host
+      (self fromString:'ftp://www.exept.de:80/test') host
+      (self fromString:'ftp://www.exept.de/test') host
+    "
+!
+
+password
+    "answer the user part of authority"
+
+    |end start|
+
+    authority isNil ifTrue:[
+        ^ nil
+    ].
+    end := authority indexOf:$@.
+    end == 0 ifTrue:[
+        ^ nil
+    ].
+    start := authority indexOf:$:.
+    (start == 0 or:[start > end]) ifTrue:[
+        ^ nil
+    ].
+    ^ authority copyFrom:start+1 to:end-1
+
+    "
+      (self fromString:'ftp://stefan@www.exept.de:80/test') password
+      (self fromString:'ftp://stefan:pass@www.exept.de:80/test') password
+      (self fromString:'ftp://www.exept.de:80/test') password
+    "
+!
+
+port
+    "answer the port part of authority"
+
+    |start|
+
+    authority isNil ifTrue:[
+        ^ self defaultPort
+    ].
+    start := authority indexOf:$@.
+    start := authority indexOf:$: startingAt:start.
+    start == 0 ifTrue:[
+        ^ self defaultPort
+    ].
+
+    ^ authority copyFrom:start+1
+
+    "
+      (self fromString:'ftp://stefan@www.exept.de:80/test') port
+      (self fromString:'ftp://www.exept.de:80/test') port
+      (self fromString:'ftp://www.exept.de/test') port
+    "
+!
+
+user
+    "answer the user part of authority"
+
+    |end end1|
+
+    authority isNil ifTrue:[
+        ^ nil
+    ].
+    end := authority indexOf:$@.
+    end == 0 ifTrue:[
+        ^ nil
+    ].
+    end1 := authority indexOf:$:.
+    end1 ~~ 0 ifTrue:[
+        end := end min:end1.
+    ].
+    ^ authority copyTo:end-1
+
+    "
+      (self fromString:'ftp://stefan@www.exept.de:80/test') user
+      (self fromString:'ftp://stefan:pass@www.exept.de:80/test') user
+      (self fromString:'ftp://www.exept.de:80/test') user
+    "
+! !
+
+!HierarchicalURI methodsFor:'defaults'!
+
+defaultPort
+   "answer the default port for the given scheme.
+    Concrete subclasses redefine this method"
+
+   ^ nil
+! !
+
+!HierarchicalURI methodsFor:'escape'!
+
+unEscape
+    "convert escaped characters (such as %20 for ' ') to their native
+     representation"
+
+    authority := self class unEscape:authority readStream.
+    pathSegments keysAndValuesDo:[:i :v|
+        pathSegments at:i put:(self class unEscape:v readStream).
+    ].
+    query := self class unEscape:query readStream.
+    fragment := self class unEscape:fragment readStream.
+! !
+
+!HierarchicalURI methodsFor:'initialize'!
+
+fromString:aString
+
+    |i i1 separator|
+
+    (aString startsWith:'//') ifTrue:[
+        i := aString indexOfAny:'/?#' startingAt:3.
+        i == 0 ifTrue:[
+            authority := aString copyFrom:3.
+            ^ self.
+        ] ifFalse:[
+            separator := aString at:i.
+            authority := aString copyFrom:3 to:i-1.
+        ].
+        isAbsolute := true.
+    ] ifFalse:[
+        (isAbsolute := aString startsWith:$/) ifTrue:[
+            i := 1.
+        ] ifFalse:[
+            i := 0.
+        ]
+    ].
+
+    [
+        i1 := aString indexOfAny:'/?#' startingAt:i+1.
+        i1 == 0 ifTrue:[
+            pathSegments add:(aString copyFrom:i+1).
+            ^ self.
+        ] ifFalse:[
+            separator := aString at:i1.
+            pathSegments add:(aString copyFrom:i+1 to:i1-1).
+            i := i1.
+        ].
+    ] doWhile:[separator == $/].
+
+    separator == $? ifTrue:[
+        i1 := aString indexOf:$# startingAt:i+1.
+        i1 == 0 ifTrue:[
+            query := aString copyFrom:i+1.
+            ^ self.
+        ] ifFalse:[
+            separator := aString at:i1.
+            query := aString copyFrom:i+1 to:i1-1.
+            i := i1.
+        ].
+    ].
+    separator == $# ifTrue:[
+        fragment := aString copyFrom:i+1.
+    ].
+        
+    "
+     self new fromString:'//authority/path1/path2'
+     self new fromString:'//authority/path1/path2?query'
+     self new fromString:'//authority/path1/path2?query#fragment'
+     self new fromString:'/path1/path2?query#fragment'
+     self new fromString:'/path1/path2#fragment'
+     self new fromString:'path1/path2#fragment'
+    "    
+!
+
+initialize
+
+    pathSegments := OrderedCollection new.
+! !
+
+!HierarchicalURI methodsFor:'printing & storing'!
+
+path
+    "answer the path part of the URI"
+
+    |aStream|
+
+    aStream := WriteStream on:''.
+
+    pathSegments size ~~ 0 ifTrue: [
+        isAbsolute ifTrue:[
+            aStream nextPut:$/.
+        ].
+        pathSegments do:[:p|
+            self class escape:p allow:';:@&=+",' on:aStream
+        ] separatedBy:[
+            aStream nextPut:$/
+        ].
+    ].
+    query notNil ifTrue: [
+        aStream nextPut:$?.
+        self class escape:query allow:nil on:aStream
+    ].
+    fragment notNil ifTrue: [
+        aStream nextPut:$#.
+        self class escape:fragment allow:nil on:aStream
+    ].
+
+    ^ aStream contents
+!
+
+printOn:aStream
+
+    self printOn:aStream escape:false
+!
+
+printOn:aStream escape:doEscape
+    "print the URI on aStream. If doEscape is set, escape special
+     characters"
+
+    super printOn:aStream.
+    authority notNil ifTrue: [
+        aStream nextPutAll:'//'.
+        doEscape ifTrue:[
+            self class escape:authority allow:'$,;:@&=+' on:aStream
+        ] ifFalse:[
+            aStream nextPutAll:authority
+        ]
+    ].
+    pathSegments size ~~ 0 ifTrue: [
+        isAbsolute ifTrue:[
+            aStream nextPut:$/.
+        ].
+        pathSegments do:[:p|
+            doEscape ifTrue:[
+                self class escape:p allow:';:@&=+",' on:aStream
+            ] ifFalse:[
+                aStream nextPutAll:p
+            ]
+        ] separatedBy:[
+            aStream nextPut:$/
+        ].
+    ].
+    query notNil ifTrue: [
+        aStream nextPut:$?.
+        doEscape ifTrue:[
+            self class escape:query allow:nil on:aStream
+        ] ifFalse:[
+            aStream nextPutAll:query
+        ]
+
+    ].
+    fragment notNil ifTrue: [
+        aStream nextPut:$#.
+        doEscape ifTrue:[
+            self class escape:fragment allow:nil on:aStream
+        ] ifFalse:[
+            aStream nextPutAll:fragment
+        ]
+    ].
+! !
+
+!HierarchicalURI methodsFor:'resolution'!
+
+addComponent:aString
+    "concatenate aString to my path"
+
+    (aString = '..' 
+     and:[pathSegments size ~~ 0 
+     and:[pathSegments first ~= '..']]) ifTrue:[
+        pathSegments removeLast.
+    ] ifFalse:[
+        pathSegments add:aString
+    ].
+!
+
+construct:aString
+    "concatenate anUri to my path"
+
+    ^ self copy 
+        addComponent:aString;
+        yourself.
+! !
+
+!HierarchicalURI class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libbasic2/HierarchicalURI.st,v 1.1 2002-01-17 14:25:25 stefan Exp $'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HttpURI.st	Thu Jan 17 15:25:48 2002 +0100
@@ -0,0 +1,56 @@
+"{ Package: 'stx:libbasic2' }"
+
+HierarchicalURI subclass:#HttpURI
+	instanceVariableNames:''
+	classVariableNames:''
+	poolDictionaries:''
+	category:'Resources'
+!
+
+
+!HttpURI class methodsFor:'accessing'!
+
+schemes
+
+    ^ #(http)
+! !
+
+!HttpURI methodsFor:'defaults'!
+
+defaultPort
+
+    ^ 80
+! !
+
+!HttpURI methodsFor:'stream access'!
+
+readStreamDo:aBlock
+    "use HTTPInterface for now"
+
+    |response headerInfo mime|
+
+    response := HTTPInterface get:self path 
+                    fromHost:self host 
+                    port:self port
+                    accept:#('*/*')  
+                    fromDocument:nil.
+
+    headerInfo := response headerInfo.
+    mime := headerInfo at:'content-type' ifAbsent:nil.
+    mime notNil ifTrue:[
+        headerInfo at:#MIME put:mime.
+    ].
+    aBlock value:response data readStream value:headerInfo
+
+    "
+     'http://www.exept.de/' asURI readStreamDo:[:stream :attributes | 
+         self halt
+      ].
+    "
+! !
+
+!HttpURI class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libbasic2/HttpURI.st,v 1.1 2002-01-17 14:25:37 stefan Exp $'
+! !
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/URI.st	Thu Jan 17 15:25:48 2002 +0100
@@ -0,0 +1,183 @@
+"{ Package: 'stx:libbasic2' }"
+
+Object subclass:#URI
+	instanceVariableNames:'scheme'
+	classVariableNames:''
+	poolDictionaries:''
+	category:'Resources'
+!
+
+
+!URI class methodsFor:'instance creation'!
+
+fromString:aString
+    "create an URI from a given String"
+
+    |i scheme rest|
+
+    i := aString indexOf:$:.
+    i == 0 ifTrue:[
+        self error:'missing scheme in URI'
+    ].
+    scheme := aString copyFrom:1 to:i-1.
+    rest := aString copyFrom:i+1.
+    ^ (self classForScheme:scheme) scheme:scheme fromString:rest
+! !
+
+!URI class methodsFor:'accessing'!
+
+classForScheme:aString
+    "find a class for a given scheme name aString"
+
+    |s|
+
+    s := aString asLowercase.
+
+    ^ self allSubclasses detect:[:cls| |schemes|
+        schemes := cls schemes.
+        schemes size ~~ 0 and:[schemes includes:s]
+    ] ifNone:[HierarchicalURI]
+!
+
+schemes
+    "answer the schemes supported by an URI-class.
+     Concrete subclasses redefine this to answer an array of scheme names"
+
+    ^ nil
+! !
+
+!URI class methodsFor:'escape'!
+
+escape:aString allow:additionalCharacters on:aStream
+
+    |val|
+
+    aString do:[:c|
+        ((c isLetterOrDigit) 
+         or:[('-_.!!~*''()' includes:c) 
+         or:[additionalCharacters notNil 
+             and:[additionalCharacters includes:c]]]
+        ) ifTrue:[
+            aStream nextPut:c
+        ] ifFalse:[
+            val := c asciiValue.            
+            aStream nextPut:$%; 
+                    nextPut:(Character digitValue:val//16);
+                    nextPut:(Character digitValue:val\\16).
+        ].
+    ].
+        
+
+    "
+     self escape:'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+          allow:nil on:TextCollector open
+
+     self escape:'Ein text mit blanks und @ & #' 
+          allow:nil on:TextCollector open
+    "  
+!
+
+unEscape:aStream
+    "convert escape sequences to the original characters"
+
+    |s c1 c2|
+
+    s := WriteStream on:''.
+
+    [aStream atEnd] whileFalse:[
+        c1 := aStream next.
+        c1 == $% ifTrue:[
+            c1 := aStream next.
+            c1 isNil ifTrue:[
+                self error:'escape sequence incomplete'
+            ].
+            c2 := aStream next.
+            c2 isNil ifTrue:[
+                self error:'escape sequence incomplete'
+            ].
+            c1 := c1 digitValue.
+            c2 := c2 digitValue.
+            (c1 > 15 or:[c2 > 15]) ifTrue:[
+                self error:'escape sequence: expect hex digit'
+            ].
+            c1 := Character value:c1*16 + c2.
+        ].
+        s nextPut:c1.
+    ].
+
+    ^ s contents.
+
+    "
+     self unEscape:(self escape:'   &%@   ' allow:nil) readStream
+    "
+! !
+
+!URI methodsFor:'accessing'!
+
+scheme
+    "return primary scheme of the class.
+     Concrete subclasses may redefine this"
+
+    |schemes|
+
+    schemes := self class schemes.
+    ^ schemes size ~~ 0 ifTrue:[
+        schemes at:1
+    ] ifFalse:[
+        nil
+    ]
+!
+
+scheme:aString
+    "set the scheme. This is a noop here and may be 
+     defined by subclasses"
+
+    ^ self
+! !
+
+!URI methodsFor:'comparing'!
+
+= anURI
+    ^ self class == anURI class and:[self asString == anURI asString]
+!
+
+hash
+    ^ self asString hash
+! !
+
+!URI methodsFor:'converting'!
+
+asURI
+
+    ^ self
+! !
+
+!URI methodsFor:'printing & storing'!
+
+printOn:aStream
+
+    |scheme|
+
+    scheme := self scheme.
+    scheme size ~~ 0 ifTrue:[
+        aStream nextPutAll:scheme; nextPut:$:
+    ].
+! !
+
+!URI methodsFor:'subclass responsibility'!
+
+readStreamDo:aBlock
+
+    "evaluate a block with the read stream as first argument
+     and a dictionary containing attributes as second argument.
+     The stream is closed after aBlock has been evaluated.
+     Attributes may be the mime type (key #MIME)"
+
+    ^ self subclassResponsibility
+! !
+
+!URI class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libbasic2/URI.st,v 1.1 2002-01-17 14:25:48 stefan Exp $'
+! !