Commit 4c0aed08 authored by Ricki Hirner's avatar Ricki Hirner

Optimizations

* use singleton objects instead of companion objects whenever possible
* use Kotlin stdlib instead of Apache Commons Lang (StringUtils)
parent b440bb0e
Pipeline #8917163 passed with stage
in 4 minutes and 47 seconds
/*
* Copyright © Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
buildscript {
ext.kotlin_version = '1.1.2-4'
......@@ -57,7 +49,6 @@ android {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile 'com.android.support:support-annotations:25.+'
compile 'org.apache.commons:commons-lang3:3.5'
compile 'com.squareup.okhttp3:okhttp:3.8.0'
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.8.0'
......
......@@ -11,7 +11,6 @@ package at.bitfire.dav4android
import okhttp3.*
import okio.Buffer
import okio.ByteString
import org.apache.commons.lang3.StringUtils
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.charset.Charset
......@@ -222,7 +221,7 @@ class BasicDigestAuthHandler(
return if (response != null) {
params.add("response=" + quotedString(response))
request.newBuilder()
.header(HEADER_AUTHORIZATION, "Digest " + StringUtils.join(params, ", "))
.header(HEADER_AUTHORIZATION, "Digest " + params.joinToString(", "))
.build()
} else
null
......
......@@ -10,11 +10,9 @@ package at.bitfire.dav4android
import java.util.logging.Logger
class Constants {
companion object {
object Constants {
@JvmField
var log = Logger.getLogger("dav4android")!!
@JvmField
var log = Logger.getLogger("dav4android")!!
}
}
}
\ No newline at end of file
......@@ -42,12 +42,10 @@ open class DavResource @JvmOverloads constructor(
val MAX_REDIRECTS = 5
val capabilities = HashSet<String>()
val capabilities = mutableSetOf<String>()
val properties = PropertyCollection()
val members = HashSet<DavResource>()
val related = HashSet<DavResource>()
val registry = PropertyRegistry
val members = mutableSetOf<DavResource>()
val related = mutableSetOf<DavResource>()
init {
......@@ -389,7 +387,7 @@ open class DavResource @JvmOverloads constructor(
while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
if (eventType == XmlPullParser.START_TAG && parser.depth == depth+1) {
val name = Property.Name(parser.namespace, parser.name)
val property = registry.create(name, parser)
val property = PropertyRegistry.create(name, parser)
if (property != null)
prop[name] = property
else
......
......@@ -9,114 +9,111 @@
package at.bitfire.dav4android
import okhttp3.Response
import org.apache.commons.lang3.StringUtils
import java.util.LinkedList
import java.util.regex.Pattern
import kotlin.collections.HashMap
class HttpUtils {
companion object {
object HttpUtils {
private val authSchemeWithParam = Pattern.compile("^([^ \"]+) +(.*)$")
private val authSchemeWithParam = Pattern.compile("^([^ \"]+) +(.*)$")
@JvmStatic
fun listHeader(response: Response, name: String): Array<String> {
val value = StringUtils.join(response.headers(name), ",")
return StringUtils.splitByWholeSeparator(value, ",")
}
@JvmStatic
fun parseWwwAuthenticate(wwwAuths: List<String>): List<AuthScheme> {
/* WWW-Authenticate = "WWW-Authenticate" ":" 1#challenge
challenge = auth-scheme 1*SP 1#auth-param
auth-scheme = token
auth-param = token "=" ( token | quoted-string )
We call the auth-param tokens: <name>=<value>
token = 1*<any CHAR except CTLs or separators>
separators = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext = <any TEXT except <">>
quoted-pair = "\" CHAR
*/
val schemes = LinkedList<AuthScheme>()
for (wwwAuth in wwwAuths) {
// Step 1: tokenize by ',', but take into account that auth-param values may contain quoted-pair values with ',' in it (these ',' have to be ignored)
// Auth-scheme and auth-param names are tokens and thus must not contain the '"' separator.
val tokens = LinkedList<String>()
var token = StringBuilder()
var inQuotes = false
val len = wwwAuth.length
var i = 0
while (i < len) {
var c = wwwAuth[i]
var literal = false
if (c == '"')
inQuotes = !inQuotes
else if (inQuotes && c == '\\' && i + 1 < len) {
token.append(c)
c = wwwAuth[++i]
literal = true
}
if (c == ',' && !inQuotes && !literal) {
tokens.add(token.toString())
token = StringBuilder()
} else
token.append(c)
@JvmStatic
fun listHeader(response: Response, name: String): Array<String> {
val value = response.headers(name).joinToString(",")
return value.split(',').filter { it.isNotEmpty() }.toTypedArray()
}
i++
@JvmStatic
fun parseWwwAuthenticate(wwwAuths: List<String>): List<AuthScheme> {
/* WWW-Authenticate = "WWW-Authenticate" ":" 1#challenge
challenge = auth-scheme 1*SP 1#auth-param
auth-scheme = token
auth-param = token "=" ( token | quoted-string )
We call the auth-param tokens: <name>=<value>
token = 1*<any CHAR except CTLs or separators>
separators = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext = <any TEXT except <">>
quoted-pair = "\" CHAR
*/
val schemes = LinkedList<AuthScheme>()
for (wwwAuth in wwwAuths) {
// Step 1: tokenize by ',', but take into account that auth-param values may contain quoted-pair values with ',' in it (these ',' have to be ignored)
// Auth-scheme and auth-param names are tokens and thus must not contain the '"' separator.
val tokens = LinkedList<String>()
var token = StringBuilder()
var inQuotes = false
val len = wwwAuth.length
var i = 0
while (i < len) {
var c = wwwAuth[i]
var literal = false
if (c == '"')
inQuotes = !inQuotes
else if (inQuotes && c == '\\' && i + 1 < len) {
token.append(c)
c = wwwAuth[++i]
literal = true
}
if (token.isNotEmpty())
if (c == ',' && !inQuotes && !literal) {
tokens.add(token.toString())
token = StringBuilder()
} else
token.append(c)
/* Step 2: determine token type after trimming:
"<authSchemes> <auth-param>" new auth scheme + 1 param
"<auth-param>" add param to previous auth scheme
Take into account that the second type may contain quoted spaces.
The auth scheme name must not contain separators (including quotes).
*/
var scheme: AuthScheme? = null
for (s in tokens) {
@Suppress("NAME_SHADOWING")
val s: String = s.trim()
val matcher = authSchemeWithParam.matcher(s)
when {
matcher.matches() -> {
// auth-scheme with auth-param
scheme = AuthScheme(matcher.group(1))
schemes.add(scheme)
scheme.addRawParam(matcher.group(2))
}
scheme != null ->
// if there was an auth-scheme before, this must be an auth-param
scheme.addRawParam(s)
else -> {
// there was not auth-scheme before, so this must be an auth-scheme
scheme = AuthScheme(s)
schemes.add(scheme)
}
i++
}
if (token.isNotEmpty())
tokens.add(token.toString())
/* Step 2: determine token type after trimming:
"<authSchemes> <auth-param>" new auth scheme + 1 param
"<auth-param>" add param to previous auth scheme
Take into account that the second type may contain quoted spaces.
The auth scheme name must not contain separators (including quotes).
*/
var scheme: AuthScheme? = null
for (s in tokens) {
@Suppress("NAME_SHADOWING")
val s: String = s.trim()
val matcher = authSchemeWithParam.matcher(s)
when {
matcher.matches() -> {
// auth-scheme with auth-param
scheme = AuthScheme(matcher.group(1))
schemes.add(scheme)
scheme.addRawParam(matcher.group(2))
}
scheme != null ->
// if there was an auth-scheme before, this must be an auth-param
scheme.addRawParam(s)
else -> {
// there was not auth-scheme before, so this must be an auth-scheme
scheme = AuthScheme(s)
schemes.add(scheme)
}
}
}
}
Constants.log.finer("Server authentication schemes: ")
for (scheme in schemes)
Constants.log.finer(" - $scheme")
Constants.log.finer("Server authentication schemes: ")
for (scheme in schemes)
Constants.log.finer(" - $scheme")
return schemes
}
return schemes
}
......@@ -136,7 +133,7 @@ class HttpUtils {
var value = m.group(2)
val len = value.length
if (value[0] == '"' && value[len - 1] == '"')
// quoted-string
// quoted-string
value = value
.substring(1, len - 1)
.replace("\\\"", "\"")
......@@ -153,6 +150,7 @@ class HttpUtils {
s.append(")")
return s.toString()
}
}
}
}
\ No newline at end of file
......@@ -8,42 +8,40 @@
package at.bitfire.dav4android;
class QuotedStringUtils {
companion object {
object QuotedStringUtils {
@JvmStatic
fun asQuotedString(raw: String?): String? {
return if (raw == null)
null
else
"\"" + raw.replace("\\" ,"\\\\").replace("\"", "\\\"") + "\""
}
@JvmStatic
fun decodeQuotedString(quoted: String?): String? {
if (quoted == null)
return null
@JvmStatic
fun asQuotedString(raw: String?): String? {
return if (raw == null)
null
else
"\"" + raw.replace("\\" ,"\\\\").replace("\"", "\\\"") + "\""
}
/* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext = <any TEXT except <">>
quoted-pair = "\" CHAR
*/
@JvmStatic
fun decodeQuotedString(quoted: String?): String? {
if (quoted == null)
return null
val len = quoted.length
if (len >= 2 && quoted[0] == '"' && quoted[len-1] == '"') {
val result = StringBuffer(len)
var pos = 1
while (pos < len-1) {
var c = quoted[pos]
if (c == '\\' && pos != len-2)
c = quoted[++pos]
result.append(c)
pos++
}
return result.toString()
} else
return quoted
}
/* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext = <any TEXT except <">>
quoted-pair = "\" CHAR
*/
val len = quoted.length
if (len >= 2 && quoted[0] == '"' && quoted[len-1] == '"') {
val result = StringBuffer(len)
var pos = 1
while (pos < len-1) {
var c = quoted[pos]
if (c == '\\' && pos != len-2)
c = quoted[++pos]
result.append(c)
pos++
}
return result.toString()
} else
return quoted
}
}
......@@ -9,69 +9,66 @@
package at.bitfire.dav4android;
import okhttp3.HttpUrl
import org.apache.commons.lang3.StringUtils
import java.net.URI
import java.net.URISyntaxException
class UrlUtils {
companion object {
object UrlUtils {
@JvmStatic
fun equals(url1: HttpUrl, url2: HttpUrl): Boolean {
// if okhttp thinks the two URLs are equal, they're in any case
// (and it's a simple String comparison)
if (url1 == url2)
return true
@JvmStatic
fun equals(url1: HttpUrl, url2: HttpUrl): Boolean {
// if okhttp thinks the two URLs are equal, they're in any case
// (and it's a simple String comparison)
if (url1 == url2)
return true
val uri1 = url1.uri()
val uri2 = url2.uri()
try {
val decoded1 = URI(uri1.scheme, uri1.schemeSpecificPart, uri1.fragment)
val decoded2 = URI (uri2.scheme, uri2.schemeSpecificPart, uri2.fragment)
return decoded1 == decoded2
} catch (e: URISyntaxException) {
return false
}
val uri1 = url1.uri()
val uri2 = url2.uri()
try {
val decoded1 = URI(uri1.scheme, uri1.schemeSpecificPart, uri1.fragment)
val decoded2 = URI (uri2.scheme, uri2.schemeSpecificPart, uri2.fragment)
return decoded1 == decoded2
} catch (e: URISyntaxException) {
return false
}
}
@JvmStatic
fun hostToDomain(host: String?): String? {
if (host == null)
return null
// remove optional dot at end
@Suppress("NAME_SHADOWING")
val host = StringUtils.removeEnd(host, ".")
@JvmStatic
fun hostToDomain(host: String?): String? {
if (host == null)
return null
// split into labels
val labels = StringUtils.split(host, '.')
return if (labels.size >= 2) {
labels[labels.size - 2] + "." + labels[labels.size - 1]
} else
host
}
// remove optional dot at end
@Suppress("NAME_SHADOWING")
val host = host.removeSuffix(".")
@JvmStatic
fun omitTrailingSlash(url: HttpUrl): HttpUrl {
val idxLast = url.pathSize () - 1
val hasTrailingSlash = url.pathSegments()[idxLast] == ""
// split into labels
val labels = host.split('.')
return if (labels.size >= 2) {
labels[labels.size - 2] + "." + labels[labels.size - 1]
} else
host
}
return if (hasTrailingSlash)
url.newBuilder().removePathSegment(idxLast).build()
else
url
}
@JvmStatic
fun omitTrailingSlash(url: HttpUrl): HttpUrl {
val idxLast = url.pathSize () - 1
val hasTrailingSlash = url.pathSegments()[idxLast] == ""
@JvmStatic
fun withTrailingSlash(url: HttpUrl): HttpUrl {
val idxLast = url.pathSize() - 1
val hasTrailingSlash = url.pathSegments()[idxLast] == ""
return if (hasTrailingSlash)
url.newBuilder().removePathSegment(idxLast).build()
else
url
}
return if (hasTrailingSlash)
url
else
url.newBuilder().addPathSegment("").build()
}
@JvmStatic
fun withTrailingSlash(url: HttpUrl): HttpUrl {
val idxLast = url.pathSize() - 1
val hasTrailingSlash = url.pathSegments()[idxLast] == ""
return if (hasTrailingSlash)
url
else
url.newBuilder().addPathSegment("").build()
}
}
......@@ -14,68 +14,66 @@ import org.xmlpull.v1.XmlPullParserFactory
import java.io.IOException
import java.util.logging.Level
class XmlUtils {
companion object {
object XmlUtils {
@JvmField val NS_WEBDAV = "DAV:"
@JvmField val NS_CALDAV = "urn:ietf:params:xml:ns:caldav"
@JvmField val NS_CARDDAV = "urn:ietf:params:xml:ns:carddav"
@JvmField val NS_APPLE_ICAL = "http://apple.com/ns/ical/"
@JvmField val NS_CALENDARSERVER = "http://calendarserver.org/ns/"
@JvmField val NS_WEBDAV = "DAV:"
@JvmField val NS_CALDAV = "urn:ietf:params:xml:ns:caldav"
@JvmField val NS_CARDDAV = "urn:ietf:params:xml:ns:carddav"
@JvmField val NS_APPLE_ICAL = "http://apple.com/ns/ical/"
@JvmField val NS_CALENDARSERVER = "http://calendarserver.org/ns/"
private val factory: XmlPullParserFactory
init {
try {
factory = XmlPullParserFactory.newInstance()
factory.isNamespaceAware = true
} catch (e: XmlPullParserException) {
throw RuntimeException("Couldn't create XmlPullParserFactory", e)
}
private val factory: XmlPullParserFactory
init {
try {
factory = XmlPullParserFactory.newInstance()
factory.isNamespaceAware = true
} catch (e: XmlPullParserException) {
throw RuntimeException("Couldn't create XmlPullParserFactory", e)
}
}
@JvmStatic
fun newPullParser() = factory.newPullParser()!!
@JvmStatic
fun newPullParser() = factory.newPullParser()!!
@JvmStatic
fun newSerializer() = factory.newSerializer()!!
@JvmStatic
fun newSerializer() = factory.newSerializer()!!
@Throws(IOException::class)
fun readText(parser: XmlPullParser): String? {
var text: String? = null
@Throws(IOException::class)
fun readText(parser: XmlPullParser): String? {
var text: String? = null
try {
val depth = parser.depth
try {
val depth = parser.depth
var eventType = parser.eventType
while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
if (eventType == XmlPullParser.TEXT && parser.depth == depth)
text = parser.text
eventType = parser.next()
}
} catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse text property", e);
var eventType = parser.eventType
while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
if (eventType == XmlPullParser.TEXT && parser.depth == depth)
text = parser.text
eventType = parser.next()
}
return text
} catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse text property", e);
}
@Throws(IOException::class)
fun readTextPropertyList(parser: XmlPullParser, name: Property.Name, list: MutableList<String>) {
try {
val depth = parser.depth
return text
}
var eventType = parser.eventType
while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
if (eventType == XmlPullParser.START_TAG && parser.depth == depth+1 &&
Property.Name(parser.namespace, parser.name) == name)
list.add(parser.nextText())
eventType = parser.next()
}
} catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse text property list", e)
@Throws(IOException::class)
fun readTextPropertyList(parser: XmlPullParser, name: Property.Name, list: MutableList<String>) {
try {
val depth = parser.depth
var eventType = parser.eventType
while (!(eventType == XmlPullParser.END_TAG && parser.depth == depth)) {
if (eventType == XmlPullParser.START_TAG && parser.depth == depth+1 &&
Property.Name(parser.namespace, parser.name) == name)
list.add(parser.nextText())
eventType = parser.next()
}
} catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse text property list", e)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment