@@ -54,7 +54,7 @@ final class URI(origStr: String) extends Serializable with Comparable[URI] {
54
54
private val _authority = fld(AbsAuthority , RelAuthority ).filter(_ != " " )
55
55
private val _userInfo = fld(AbsUserInfo , RelUserInfo )
56
56
private val _host = fld(AbsHost , RelHost )
57
- private val _port = fld(AbsPort , RelPort ).fold( - 1 ) (_.toInt)
57
+ private val _port = fld(AbsPort , RelPort ).map (_.toInt)
58
58
59
59
private val _path = {
60
60
val useNetPath = fld(AbsAuthority , RelAuthority ).isDefined
@@ -101,41 +101,56 @@ final class URI(origStr: String) extends Serializable with Comparable[URI] {
101
101
*/
102
102
@ inline
103
103
private def internalCompare (that : URI )(cmp : (String , String ) => Int ): Int = {
104
- @ inline def cmpOpt (x : js.UndefOr [String ], y : js.UndefOr [String ]): Int = {
104
+ @ inline
105
+ def cmpOpt [T ](x : js.UndefOr [T ], y : js.UndefOr [T ])(comparator : (T , T ) => Int ): Int = {
105
106
if (x == y) 0
106
107
// Undefined components are considered less than defined components
107
- else x.fold(- 1 )(s1 => y.fold(1 )(s2 => cmp(s1, s2)))
108
+ else x.fold(- 1 )(s1 => y.fold(1 )(s2 => comparator(s1, s2)))
109
+ }
110
+ def comparePathQueryFragement (): Int = {
111
+ val cmpPath = cmpOpt(this ._path, that._path)(cmp)
112
+ if (cmpPath != 0 ) {
113
+ cmpPath
114
+ } else {
115
+ val cmpQuery = cmpOpt(this ._query, that._query)(cmp)
116
+ if (cmpQuery != 0 ) cmpQuery
117
+ else cmpOpt(this ._fragment, that._fragment)(cmp)
118
+ }
108
119
}
109
120
110
- if (this ._scheme != that._scheme)
111
- this ._scheme.fold(- 1 )(s1 => that._scheme.fold(1 )(s1.compareToIgnoreCase))
112
- else if (this ._isOpaque != that._isOpaque)
113
- // A hierarchical URI is less than an opaque URI
114
- if (this ._isOpaque) 1 else - 1
115
- else if (_isOpaque) {
116
- val ssp = cmp(this ._schemeSpecificPart, that._schemeSpecificPart)
117
- if (ssp != 0 ) ssp
118
- else cmpOpt(this ._fragment, that._fragment)
119
- } else if (this ._authority != that._authority) {
120
- if (this ._host.isDefined && that._host.isDefined) {
121
- val ui = cmpOpt(this ._userInfo, that._userInfo)
122
- if (ui != 0 ) ui
123
- else {
124
- val hst = this ._host.get.compareToIgnoreCase(that._host.get)
125
- if (hst != 0 ) hst
126
- else if (this ._port == that._port) 0
127
- else if (this ._port == - 1 ) - 1
128
- else if (that._port == - 1 ) 1
129
- else this ._port - that._port
121
+ val cmpScheme = cmpOpt(this ._scheme, that._scheme)(_.compareToIgnoreCase(_))
122
+ if (cmpScheme != 0 ) {
123
+ cmpScheme
124
+ } else {
125
+ val cmpIsOpaque = this .isOpaque.compareTo(that.isOpaque) // A hierarchical URI is less than an opaque URI
126
+ if (cmpIsOpaque != 0 ) {
127
+ cmpIsOpaque
128
+ } else {
129
+ if (this .isOpaque()) {
130
+ val cmpSchemeSpecificPart = cmp(this ._schemeSpecificPart, that._schemeSpecificPart)
131
+ if (cmpSchemeSpecificPart != 0 ) cmpSchemeSpecificPart
132
+ else comparePathQueryFragement()
133
+ } else if (this ._host.isDefined && that._host.isDefined) {
134
+ val cmpUserInfo = cmpOpt(this ._userInfo, that._userInfo)(cmp)
135
+ if (cmpUserInfo != 0 ) {
136
+ cmpUserInfo
137
+ } else {
138
+ val cmpHost = cmpOpt(this ._host, that._host)(_.compareToIgnoreCase(_))
139
+ if (cmpHost != 0 ) {
140
+ cmpHost
141
+ } else {
142
+ val cmpPort = cmpOpt(this ._port, that._port)(_ - _)
143
+ if (cmpPort != 0 ) cmpPort
144
+ else comparePathQueryFragement()
145
+ }
146
+ }
147
+ } else {
148
+ val cmpAuthority = cmpOpt(this ._authority, that._authority)(cmp)
149
+ if (cmpAuthority != 0 ) cmpAuthority
150
+ else comparePathQueryFragement()
130
151
}
131
- } else
132
- cmpOpt(this ._authority, that._authority)
133
- } else if (this ._path != that._path)
134
- cmpOpt(this ._path, that._path)
135
- else if (this ._query != that._query)
136
- cmpOpt(this ._query, that._query)
137
- else
138
- cmpOpt(this ._fragment, that._fragment)
152
+ }
153
+ }
139
154
}
140
155
141
156
def compareTo (that : URI ): Int = internalCompare(that)(_.compareTo(_))
@@ -149,7 +164,7 @@ final class URI(origStr: String) extends Serializable with Comparable[URI] {
149
164
def getFragment (): String = _fragment.map(decodeComponent).orNull
150
165
def getHost (): String = _host.orNull
151
166
def getPath (): String = _path.map(decodeComponent).orNull
152
- def getPort (): Int = _port
167
+ def getPort (): Int = _port.getOrElse( - 1 )
153
168
def getQuery (): String = _query.map(decodeComponent).orNull
154
169
def getRawAuthority (): String = _authority.orNull
155
170
def getRawFragment (): String = _fragment.orNull
@@ -166,10 +181,19 @@ final class URI(origStr: String) extends Serializable with Comparable[URI] {
166
181
import URI .normalizeEscapes
167
182
168
183
var acc = URI .uriSeed
169
- acc = mix(acc, _scheme.## ) // scheme may not contain escapes
170
- acc = mix(acc, normalizeEscapes(_schemeSpecificPart).## )
171
- acc = mixLast(acc, _fragment.map(normalizeEscapes).## )
172
-
184
+ acc = mix(acc, _scheme.map(_.toLowerCase).## ) // scheme may not contain escapes
185
+ if (this .isOpaque()) {
186
+ acc = mix(acc, normalizeEscapes(this ._schemeSpecificPart).## )
187
+ } else if (this ._host.isDefined) {
188
+ acc = mix(acc, normalizeEscapes(this ._userInfo).## )
189
+ acc = mix(acc, this ._host.map(_.toLowerCase).## )
190
+ acc = mix(acc, this ._port.## )
191
+ } else {
192
+ acc = mix(acc, normalizeEscapes(this ._authority).## )
193
+ }
194
+ acc = mix(acc, normalizeEscapes(this ._path).## )
195
+ acc = mix(acc, normalizeEscapes(this ._query).## )
196
+ acc = mixLast(acc, normalizeEscapes(this ._fragment).## )
173
197
finalizeHash(acc, 3 )
174
198
}
175
199
@@ -334,7 +358,10 @@ object URI {
334
358
}
335
359
336
360
// IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
337
- private final val ipv4address = " [0-9]{1,3}(?:\\ .[0-9]{1,3}){3}"
361
+ private final val ipv4address = {
362
+ val digit = " (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
363
+ s " (?: $digit\\ .){3} $digit"
364
+ }
338
365
339
366
private final val ipv6address = {
340
367
// http://stackoverflow.com/a/17871737/1149944
@@ -800,21 +827,22 @@ object URI {
800
827
}
801
828
802
829
/** Upper-cases all URI escape sequences in `str`. Used for hashing */
803
- private def normalizeEscapes (str : String ): String = {
804
- var i = 0
805
- var res = " "
806
- while (i < str.length) {
807
- if (str.charAt(i) == '%' ) {
808
- assert(str.length > i + 2 , " Invalid escape in URI" )
809
- res += str.substring(i, i+ 3 ).toUpperCase()
810
- i += 3
811
- } else {
812
- res += str.substring(i, i+ 1 )
813
- i += 1
830
+ private def normalizeEscapes (maybeStr : js.UndefOr [String ]): js.UndefOr [String ] = {
831
+ maybeStr.map { str =>
832
+ var i = 0
833
+ var res = " "
834
+ while (i < str.length) {
835
+ if (str.charAt(i) == '%' ) {
836
+ assert(str.length > i + 2 , " Invalid escape in URI" )
837
+ res += str.substring(i, i+ 3 ).toUpperCase()
838
+ i += 3
839
+ } else {
840
+ res += str.substring(i, i+ 1 )
841
+ i += 1
842
+ }
814
843
}
844
+ res
815
845
}
816
-
817
- res
818
846
}
819
847
820
848
private final val uriSeed = 53722356
0 commit comments