Commit b440bb0e authored by Ricki Hirner's avatar Ricki Hirner

Optimizations

parent eec3f71b
Pipeline #8896083 passed with stage
in 4 minutes and 12 seconds
...@@ -96,13 +96,13 @@ open class DavResource @JvmOverloads constructor( ...@@ -96,13 +96,13 @@ open class DavResource @JvmOverloads constructor(
val rqBody = if (xmlBody != null) RequestBody.create(MIME_XML, xmlBody) else null val rqBody = if (xmlBody != null) RequestBody.create(MIME_XML, xmlBody) else null
var response: Response? = null var response: Response? = null
for (attempt in 0..MAX_REDIRECTS-1) { for (attempt in 1..MAX_REDIRECTS) {
response = httpClient.newCall(Request.Builder() response = httpClient.newCall(Request.Builder()
.method("MKCOL", rqBody) .method("MKCOL", rqBody)
.url(location) .url(location)
.build()).execute() .build()).execute()
if (response.isRedirect) if (response.isRedirect)
processRedirection(response) processRedirect(response)
else else
break break
} }
...@@ -121,7 +121,7 @@ open class DavResource @JvmOverloads constructor( ...@@ -121,7 +121,7 @@ open class DavResource @JvmOverloads constructor(
@Throws(IOException::class, HttpException::class, DavException::class) @Throws(IOException::class, HttpException::class, DavException::class)
fun get(accept: String): ResponseBody { fun get(accept: String): ResponseBody {
var response: Response? = null var response: Response? = null
for (attempt in 0..MAX_REDIRECTS-1) { for (attempt in 1..MAX_REDIRECTS) {
response = httpClient.newCall(Request.Builder() response = httpClient.newCall(Request.Builder()
.get() .get()
.url(location) .url(location)
...@@ -129,7 +129,7 @@ open class DavResource @JvmOverloads constructor( ...@@ -129,7 +129,7 @@ open class DavResource @JvmOverloads constructor(
.header("Accept-Encoding", "identity") // disable compression because it can change the ETag .header("Accept-Encoding", "identity") // disable compression because it can change the ETag
.build()).execute() .build()).execute()
if (response.isRedirect) if (response.isRedirect)
processRedirection(response) processRedirect(response)
else else
break break
} }
...@@ -143,9 +143,9 @@ open class DavResource @JvmOverloads constructor( ...@@ -143,9 +143,9 @@ open class DavResource @JvmOverloads constructor(
val body = response.body() ?: throw HttpException("GET without response body") val body = response.body() ?: throw HttpException("GET without response body")
val mimeType = body.contentType() body.contentType()?.let { mimeType ->
if (mimeType != null)
properties[GetContentType.NAME] = GetContentType(mimeType) properties[GetContentType.NAME] = GetContentType(mimeType)
}
return body return body
} }
...@@ -163,7 +163,7 @@ open class DavResource @JvmOverloads constructor( ...@@ -163,7 +163,7 @@ open class DavResource @JvmOverloads constructor(
fun put(body: RequestBody, ifMatchETag: String?, ifNoneMatch: Boolean): Boolean { fun put(body: RequestBody, ifMatchETag: String?, ifNoneMatch: Boolean): Boolean {
var redirected = false var redirected = false
var response: Response? = null var response: Response? = null
for (attempt in 0..MAX_REDIRECTS-1) { for (attempt in 1..MAX_REDIRECTS) {
val builder = Request.Builder() val builder = Request.Builder()
.put(body) .put(body)
.url(location) .url(location)
...@@ -177,7 +177,7 @@ open class DavResource @JvmOverloads constructor( ...@@ -177,7 +177,7 @@ open class DavResource @JvmOverloads constructor(
response = httpClient.newCall(builder.build()).execute() response = httpClient.newCall(builder.build()).execute()
if (response.isRedirect) { if (response.isRedirect) {
processRedirection(response) processRedirect(response)
redirected = true redirected = true
} else } else
break break
...@@ -202,7 +202,7 @@ open class DavResource @JvmOverloads constructor( ...@@ -202,7 +202,7 @@ open class DavResource @JvmOverloads constructor(
@Throws(IOException::class, HttpException::class) @Throws(IOException::class, HttpException::class)
fun delete(ifMatchETag: String?) { fun delete(ifMatchETag: String?) {
var response: Response? = null var response: Response? = null
for (attempt in 0..MAX_REDIRECTS-1) { for (attempt in 1..MAX_REDIRECTS) {
val builder = Request.Builder() val builder = Request.Builder()
.delete() .delete()
.url(location) .url(location)
...@@ -210,19 +210,19 @@ open class DavResource @JvmOverloads constructor( ...@@ -210,19 +210,19 @@ open class DavResource @JvmOverloads constructor(
builder.header("If-Match", QuotedStringUtils.asQuotedString(ifMatchETag)) builder.header("If-Match", QuotedStringUtils.asQuotedString(ifMatchETag))
response = httpClient.newCall(builder.build()).execute() response = httpClient.newCall(builder.build()).execute()
if (response.isRedirect) { if (response.isRedirect)
processRedirection(response) processRedirect(response)
} else else
break break
} }
checkStatus(response!!, false) checkStatus(response!!, false)
if (response.code() == 207) { if (response.code() == 207)
/* If an error occurs deleting a member resource (a resource other than /* If an error occurs deleting a member resource (a resource other than
the resource identified in the Request-URI), then the response can be the resource identified in the Request-URI), then the response can be
a 207 (Multi-Status). […] (RFC 4918 9.6.1. DELETE for Collections) */ a 207 (Multi-Status). […] (RFC 4918 9.6.1. DELETE for Collections) */
throw HttpException(response) throw HttpException(response)
} else else
response.body()?.close() response.body()?.close()
} }
...@@ -258,14 +258,14 @@ open class DavResource @JvmOverloads constructor( ...@@ -258,14 +258,14 @@ open class DavResource @JvmOverloads constructor(
serializer.endDocument() serializer.endDocument()
var response: Response? = null var response: Response? = null
for (attempt in 0..MAX_REDIRECTS-1) { for (attempt in 1..MAX_REDIRECTS) {
response = httpClient.newCall(Request.Builder() response = httpClient.newCall(Request.Builder()
.url(location) .url(location)
.method("PROPFIND", RequestBody.create(MIME_XML, writer.toString())) .method("PROPFIND", RequestBody.create(MIME_XML, writer.toString()))
.header("Depth", depth.toString()) .header("Depth", depth.toString())
.build()).execute() .build()).execute()
if (response.isRedirect) if (response.isRedirect)
processRedirection(response) processRedirect(response)
else else
break break
} }
...@@ -273,9 +273,11 @@ open class DavResource @JvmOverloads constructor( ...@@ -273,9 +273,11 @@ open class DavResource @JvmOverloads constructor(
checkStatus(response!!, false) checkStatus(response!!, false)
assertMultiStatus(response) assertMultiStatus(response)
if (depth > 0) if (depth > 0) {
// collection listing requested, drop old member information // collection listing requested, drop old member information
members.clear() members.clear()
related.clear()
}
response.body()?.charStream()?.use { processMultiStatus(it) } response.body()?.charStream()?.use { processMultiStatus(it) }
} }
...@@ -347,9 +349,10 @@ open class DavResource @JvmOverloads constructor( ...@@ -347,9 +349,10 @@ open class DavResource @JvmOverloads constructor(
} }
/** /**
* Sets the new [location] in case of a redirect. Closes the [response] body.
* @throws HttpException in case of an HTTP error * @throws HttpException in case of an HTTP error
*/ */
protected fun processRedirection(response: Response) { protected fun processRedirect(response: Response) {
try { try {
response.header("Location")?.let { response.header("Location")?.let {
val target = location.resolve(it) val target = location.resolve(it)
......
...@@ -135,13 +135,12 @@ class HttpUtils { ...@@ -135,13 +135,12 @@ class HttpUtils {
val name = m.group(1) val name = m.group(1)
var value = m.group(2) var value = m.group(2)
val len = value.length val len = value.length
if (value[0] == '"' && value[len - 1] == '"') { if (value[0] == '"' && value[len - 1] == '"')
// quoted-string // quoted-string
value = value value = value
.substring(1, len - 1) .substring(1, len - 1)
.replace("\\\"", "\"") .replace("\\\"", "\"")
} params[name.toLowerCase()] = value
params.put(name.toLowerCase(), value)
} else } else
unnamedParams.add(authParam) unnamedParams.add(authParam)
} }
......
...@@ -21,6 +21,8 @@ interface Property { ...@@ -21,6 +21,8 @@ interface Property {
super.equals(o) super.equals(o)
} }
override fun hashCode() = namespace.hashCode() xor name.hashCode()
override fun toString() = "$name($namespace)" override fun toString() = "$name($namespace)"
} }
......
...@@ -20,6 +20,7 @@ class PropertyCollection { ...@@ -20,6 +20,7 @@ class PropertyCollection {
operator fun get(name: Property.Name): Property? { operator fun get(name: Property.Name): Property? {
if (!properties.isInitialized()) if (!properties.isInitialized())
return null return null
val nsProperties = properties.value[name.namespace] ?: return null val nsProperties = properties.value[name.namespace] ?: return null
return nsProperties[name.name] return nsProperties[name.name]
} }
...@@ -27,6 +28,7 @@ class PropertyCollection { ...@@ -27,6 +28,7 @@ class PropertyCollection {
fun getMap(): Map<Property.Name, Property?> { fun getMap(): Map<Property.Name, Property?> {
if (!properties.isInitialized()) if (!properties.isInitialized())
return mapOf() return mapOf()
val map = HashMap<Property.Name, Property?>() val map = HashMap<Property.Name, Property?>()
for ((namespace, nsProperties) in properties.value) { for ((namespace, nsProperties) in properties.value) {
for ((name, property) in nsProperties) for ((name, property) in nsProperties)
...@@ -46,11 +48,17 @@ class PropertyCollection { ...@@ -46,11 +48,17 @@ class PropertyCollection {
} }
fun remove(name: Property.Name) { fun remove(name: Property.Name) {
if (!properties.isInitialized())
return
val nsProperties = properties.value[name.namespace] val nsProperties = properties.value[name.namespace]
nsProperties?.remove(name.name) nsProperties?.remove(name.name)
} }
fun size(): Int { fun size(): Int {
if (!properties.isInitialized())
return 0
var size = 0 var size = 0
for (nsProperties in properties.value.values) for (nsProperties in properties.value.values)
size += nsProperties.size size += nsProperties.size
...@@ -85,6 +93,9 @@ class PropertyCollection { ...@@ -85,6 +93,9 @@ class PropertyCollection {
} }
fun nullAllValues() { fun nullAllValues() {
if (!properties.isInitialized())
return
for ((_, nsProperties) in properties.value) { for ((_, nsProperties) in properties.value) {
for (name in nsProperties.keys) for (name in nsProperties.keys)
nsProperties.put(name, null) nsProperties.put(name, null)
...@@ -94,8 +105,7 @@ class PropertyCollection { ...@@ -94,8 +105,7 @@ class PropertyCollection {
override fun toString(): String { override fun toString(): String {
val s = LinkedList<String>() val s = LinkedList<String>()
val properties = getMap() for ((name, value) in getMap())
for ((name, value) in properties)
s.add("$name: $value") s.add("$name: $value")
return "[" + TextUtils.join(", ", s) + "]" return "[" + TextUtils.join(", ", s) + "]"
} }
......
...@@ -13,7 +13,7 @@ import java.util.* ...@@ -13,7 +13,7 @@ import java.util.*
object PropertyRegistry { object PropertyRegistry {
val factories = HashMap<String, HashMap<String, PropertyFactory>>() val factories = mutableMapOf<String /*namespace*/, MutableMap<String /*name*/, PropertyFactory>>()
init { init {
Constants.log.info("Registering DAV property factories"); Constants.log.info("Registering DAV property factories");
...@@ -28,18 +28,15 @@ object PropertyRegistry { ...@@ -28,18 +28,15 @@ object PropertyRegistry {
val name = factory.getName() val name = factory.getName()
var nsFactories = factories[name.namespace] var nsFactories = factories[name.namespace]
if (nsFactories == null) { if (nsFactories == null) {
nsFactories = HashMap<String, PropertyFactory>() nsFactories = mutableMapOf<String, PropertyFactory>()
factories[name.namespace] = nsFactories factories[name.namespace] = nsFactories
} }
nsFactories[name.name] = factory; nsFactories[name.name] = factory
} }
fun create(name: Property.Name, parser: XmlPullParser): Property? { fun create(name: Property.Name, parser: XmlPullParser): Property? {
val map = factories[name.namespace] factories[name.namespace]?.let { nsFactories ->
if (map != null) { nsFactories[name.name]?.let { factory -> return factory.create(parser) }
val factory = map[name.name]
if (factory != null)
return factory.create(parser)
} }
return null return null
} }
......
...@@ -11,6 +11,7 @@ package at.bitfire.dav4android; ...@@ -11,6 +11,7 @@ package at.bitfire.dav4android;
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParserFactory import org.xmlpull.v1.XmlPullParserFactory
import java.io.IOException
import java.util.logging.Level import java.util.logging.Level
class XmlUtils { class XmlUtils {
...@@ -39,6 +40,7 @@ class XmlUtils { ...@@ -39,6 +40,7 @@ class XmlUtils {
fun newSerializer() = factory.newSerializer()!! fun newSerializer() = factory.newSerializer()!!
@Throws(IOException::class)
fun readText(parser: XmlPullParser): String? { fun readText(parser: XmlPullParser): String? {
var text: String? = null var text: String? = null
...@@ -51,13 +53,14 @@ class XmlUtils { ...@@ -51,13 +53,14 @@ class XmlUtils {
text = parser.text text = parser.text
eventType = parser.next() eventType = parser.next()
} }
} catch(e: Exception) { } catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse text property", e); Constants.log.log(Level.SEVERE, "Couldn't parse text property", e);
} }
return text return text
} }
@Throws(IOException::class)
fun readTextPropertyList(parser: XmlPullParser, name: Property.Name, list: MutableList<String>) { fun readTextPropertyList(parser: XmlPullParser, name: Property.Name, list: MutableList<String>) {
try { try {
val depth = parser.depth val depth = parser.depth
...@@ -69,7 +72,7 @@ class XmlUtils { ...@@ -69,7 +72,7 @@ class XmlUtils {
list.add(parser.nextText()) list.add(parser.nextText())
eventType = parser.next() eventType = parser.next()
} }
} catch(e: Exception) { } catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse text property list", e) Constants.log.log(Level.SEVERE, "Couldn't parse text property list", e)
} }
} }
......
...@@ -18,9 +18,7 @@ class ServiceUnavailableException: HttpException { ...@@ -18,9 +18,7 @@ class ServiceUnavailableException: HttpException {
var retryAfter: Date? = null var retryAfter: Date? = null
constructor(message: String?): super(HttpURLConnection.HTTP_UNAVAILABLE, message) { constructor(message: String?): super(HttpURLConnection.HTTP_UNAVAILABLE, message)
retryAfter = null
}
constructor(response: Response): super(response) { constructor(response: Response): super(response) {
// Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds ) // Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
......
...@@ -8,8 +8,4 @@ ...@@ -8,8 +8,4 @@
package at.bitfire.dav4android.exception package at.bitfire.dav4android.exception
class UnsupportedDavException: DavException { class UnsupportedDavException(message: String?) : DavException(message)
\ No newline at end of file
constructor(message: String?): super(message)
}
...@@ -24,7 +24,7 @@ data class CalendarColor( ...@@ -24,7 +24,7 @@ data class CalendarColor(
@JvmField @JvmField
val NAME = Property.Name(XmlUtils.NS_APPLE_ICAL, "calendar-color") val NAME = Property.Name(XmlUtils.NS_APPLE_ICAL, "calendar-color")
val PATTERN = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?") val PATTERN = Pattern.compile("#?(\\p{XDigit}{6})(\\p{XDigit}{2})?")!!
/** /**
* Converts a WebDAV color from one of these formats: * Converts a WebDAV color from one of these formats:
......
...@@ -13,6 +13,7 @@ import at.bitfire.dav4android.Property ...@@ -13,6 +13,7 @@ import at.bitfire.dav4android.Property
import at.bitfire.dav4android.PropertyFactory import at.bitfire.dav4android.PropertyFactory
import at.bitfire.dav4android.XmlUtils import at.bitfire.dav4android.XmlUtils
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import java.util.logging.Level import java.util.logging.Level
// see RFC 5397: WebDAV Current Principal Extension // see RFC 5397: WebDAV Current Principal Extension
...@@ -45,7 +46,7 @@ data class CurrentUserPrincipal( ...@@ -45,7 +46,7 @@ data class CurrentUserPrincipal(
href = parser.nextText() href = parser.nextText()
eventType = parser.next() eventType = parser.next()
} }
} catch(e: Exception) { } catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse <current-user-principal>", e); Constants.log.log(Level.SEVERE, "Couldn't parse <current-user-principal>", e);
} }
......
...@@ -13,6 +13,7 @@ import at.bitfire.dav4android.Property ...@@ -13,6 +13,7 @@ import at.bitfire.dav4android.Property
import at.bitfire.dav4android.PropertyFactory import at.bitfire.dav4android.PropertyFactory
import at.bitfire.dav4android.XmlUtils import at.bitfire.dav4android.XmlUtils
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import java.util.logging.Level import java.util.logging.Level
data class CurrentUserPrivilegeSet( data class CurrentUserPrivilegeSet(
...@@ -66,7 +67,7 @@ data class CurrentUserPrivilegeSet( ...@@ -66,7 +67,7 @@ data class CurrentUserPrivilegeSet(
parsePrivilege() parsePrivilege()
eventType = parser.next() eventType = parser.next()
} }
} catch(e: Exception) { } catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse <current-user-privilege-set>", e) Constants.log.log(Level.SEVERE, "Couldn't parse <current-user-privilege-set>", e)
return null return null
} }
......
...@@ -28,7 +28,6 @@ class GetETag: Property { ...@@ -28,7 +28,6 @@ class GetETag: Property {
weak = "W/" weak = "W/"
opaque-tag = quoted-string opaque-tag = quoted-string
*/ */
var tag: String? = rawETag var tag: String? = rawETag
tag?.let { tag?.let {
// remove trailing "W/" // remove trailing "W/"
......
...@@ -15,8 +15,8 @@ import at.bitfire.dav4android.XmlUtils ...@@ -15,8 +15,8 @@ import at.bitfire.dav4android.XmlUtils
import okhttp3.internal.http.HttpDate import okhttp3.internal.http.HttpDate
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
class GetLastModified( data class GetLastModified(
var lastModified: Long? var lastModified: Long
): Property { ): Property {
companion object { companion object {
...@@ -24,27 +24,22 @@ class GetLastModified( ...@@ -24,27 +24,22 @@ class GetLastModified(
val NAME = Property.Name(XmlUtils.NS_WEBDAV, "getlastmodified") val NAME = Property.Name(XmlUtils.NS_WEBDAV, "getlastmodified")
} }
constructor(rawDate: String?)
: this(null as Long?)
{
if (rawDate != null) {
val date = HttpDate.parse(rawDate)
if (date != null)
lastModified = date.time
else
Constants.log.warning("Couldn't parse Last-Modified date")
} else
Constants.log.warning("Last-Modified without date")
}
class Factory: PropertyFactory { class Factory: PropertyFactory {
override fun getName() = NAME override fun getName() = NAME
override fun create(parser: XmlPullParser) = override fun create(parser: XmlPullParser): GetLastModified? {
// <!ELEMENT getlastmodified (#PCDATA) > // <!ELEMENT getlastmodified (#PCDATA) >
GetLastModified(XmlUtils.readText(parser)) XmlUtils.readText(parser)?.let { rawDate ->
val date = HttpDate.parse(rawDate)
if (date != null)
return GetLastModified(date.time)
else
Constants.log.warning("Couldn't parse Last-Modified date")
}
return null
}
} }
......
...@@ -19,7 +19,7 @@ abstract class HrefListProperty: Property { ...@@ -19,7 +19,7 @@ abstract class HrefListProperty: Property {
val hrefs = LinkedList<String>() val hrefs = LinkedList<String>()
override fun toString() = "hrefs=[" + TextUtils.join(", ", hrefs) + "]" override fun toString() = "href=[" + TextUtils.join(", ", hrefs) + "]"
abstract class Factory: PropertyFactory { abstract class Factory: PropertyFactory {
......
...@@ -14,6 +14,7 @@ import at.bitfire.dav4android.Property ...@@ -14,6 +14,7 @@ import at.bitfire.dav4android.Property
import at.bitfire.dav4android.PropertyFactory import at.bitfire.dav4android.PropertyFactory
import at.bitfire.dav4android.XmlUtils import at.bitfire.dav4android.XmlUtils
import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import java.util.logging.Level import java.util.logging.Level
class ResourceType: Property { class ResourceType: Property {
...@@ -58,7 +59,7 @@ class ResourceType: Property { ...@@ -58,7 +59,7 @@ class ResourceType: Property {
} }
eventType = parser.next() eventType = parser.next()
} }
} catch(e: Exception) { } catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse <resourcetype>", e); Constants.log.log(Level.SEVERE, "Couldn't parse <resourcetype>", e);
return null return null
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
package at.bitfire.dav4android.property package at.bitfire.dav4android.property
import android.text.TextUtils
import at.bitfire.dav4android.Constants import at.bitfire.dav4android.Constants
import at.bitfire.dav4android.Property import at.bitfire.dav4android.Property
import at.bitfire.dav4android.PropertyFactory import at.bitfire.dav4android.PropertyFactory
...@@ -26,6 +27,7 @@ class SupportedAddressData: Property { ...@@ -26,6 +27,7 @@ class SupportedAddressData: Property {
val types = mutableSetOf<MediaType>() val types = mutableSetOf<MediaType>()
fun hasVCard4() = types.any { "text/vcard; version=4.0".equals(it.toString(), true) } fun hasVCard4() = types.any { "text/vcard; version=4.0".equals(it.toString(), true) }
override fun toString() = "[" + TextUtils.join(", ", types) + "]"
class Factory: PropertyFactory { class Factory: PropertyFactory {
......
...@@ -62,7 +62,7 @@ data class SupportedCalendarComponentSet( ...@@ -62,7 +62,7 @@ data class SupportedCalendarComponentSet(
} }
eventType = parser.next() eventType = parser.next()
} }
} catch(e: Exception) { } catch(e: XmlPullParserException) {
Constants.log.log(Level.SEVERE, "Couldn't parse <supported-calendar-component-set>", e) Constants.log.log(Level.SEVERE, "Couldn't parse <supported-calendar-component-set>", e)
return null return null