package com.discord.utilities.error; import android.annotation.SuppressLint; import android.content.Context; import android.widget.Toast; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; import c.a.x.a.a; import c.a.x.a.b; import com.discord.R; import com.discord.models.domain.Model; import com.discord.models.domain.ModelAuditLogEntry; import com.discord.utilities.captcha.CaptchaHelper; import com.discord.utilities.images.MGImagesBitmap; import com.discord.utilities.rest.RestAPIAbortMessages; import com.google.android.material.shadow.ShadowDrawableWrapper; import java.io.IOException; import java.io.InterruptedIOException; import java.io.StringReader; import java.net.NoRouteToHostException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.ResponseBody; import retrofit2.HttpException; import rx.functions.Action1; import rx.functions.Action3; public class Error { private static Action3> onUnhandledError = null; private static boolean unexpectedExceptionsCrashDebug = true; @Nullable private final String bodyText; @Nullable private final Map metadata; @NonNull private final Response response; @NonNull private final AtomicBoolean shouldLog = new AtomicBoolean(true); @NonNull private final AtomicBoolean showErrorToasts = new AtomicBoolean(true); @NonNull private final Throwable throwable; @NonNull private final Type type; /* renamed from: com.discord.utilities.error.Error$1 reason: invalid class name */ public static /* synthetic */ class AnonymousClass1 { public static final /* synthetic */ int[] $SwitchMap$com$discord$utilities$error$Error$Type; static { Type.values(); int[] iArr = new int[17]; $SwitchMap$com$discord$utilities$error$Error$Type = iArr; try { iArr[Type.DISCORD_REQUEST_ERROR_UNKNOWN.ordinal()] = 1; } catch (NoSuchFieldError unused) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.OTHER.ordinal()] = 2; } catch (NoSuchFieldError unused2) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.INTERMITTENT_CLOUD_FLARE.ordinal()] = 3; } catch (NoSuchFieldError unused3) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.FORBIDDEN_CLOUD_FLARE.ordinal()] = 4; } catch (NoSuchFieldError unused4) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.RATE_LIMITED.ordinal()] = 5; } catch (NoSuchFieldError unused5) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.FORBIDDEN_DISCORD.ordinal()] = 6; } catch (NoSuchFieldError unused6) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.DISCORD_REQUEST_ERROR.ordinal()] = 7; } catch (NoSuchFieldError unused7) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.DISCORD_BAD_REQUEST.ordinal()] = 8; } catch (NoSuchFieldError unused8) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.NETWORK.ordinal()] = 9; } catch (NoSuchFieldError unused9) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.SSL.ordinal()] = 10; } catch (NoSuchFieldError unused10) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.TIMEOUT.ordinal()] = 11; } catch (NoSuchFieldError unused11) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.REQUEST_TOO_LARGE.ordinal()] = 12; } catch (NoSuchFieldError unused12) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.UNAUTHORIZED.ordinal()] = 13; } catch (NoSuchFieldError unused13) { } try { $SwitchMap$com$discord$utilities$error$Error$Type[Type.INTERNAL_SERVER_ERROR.ordinal()] = 14; } catch (NoSuchFieldError unused14) { } } } public static class Response implements Model { private int code; private boolean global; @Nullable private String message; @NonNull private Map> messages; private double retryAfter; @Nullable private SkemaError skemaError; private Response(@Nullable String str) { this.messages = new HashMap(); if (str != null) { try { new Model.JsonReader(new StringReader(str)).parse(this); } catch (Exception unused) { } } } public /* synthetic */ Response(String str, AnonymousClass1 r2) { this(str); } public static /* synthetic */ String access$100(Response response, Context context, int i) { return response.getMessageToast(context, i); } @SuppressLint({"ResourceType"}) private String getMessageToast(Context context, @StringRes int i) { Integer abortCodeMessageResId = RestAPIAbortMessages.getAbortCodeMessageResId(this.code); if (abortCodeMessageResId != null) { return context.getString(abortCodeMessageResId.intValue()); } SkemaError skemaError = this.skemaError; if (skemaError != null) { if (!SkemaError.access$400(skemaError).isEmpty()) { return SkemaErrorItem.access$500((SkemaErrorItem) SkemaError.access$400(this.skemaError).get(0)); } for (SkemaError skemaError2 : SkemaError.access$600(this.skemaError).values()) { if (!SkemaError.access$400(skemaError2).isEmpty()) { return SkemaErrorItem.access$500((SkemaErrorItem) SkemaError.access$400(skemaError2).get(0)); } } } String str = this.message; if (str != null) { return str; } for (List list : this.messages.values()) { Iterator it = list.iterator(); while (true) { if (it.hasNext()) { String next = it.next(); if (next != null) { return next; } } } } return context.getString(i); } @Override // com.discord.models.domain.Model public void assignField(Model.JsonReader jsonReader) throws IOException { String nextName = jsonReader.nextName(); nextName.hashCode(); char c2 = 65535; switch (nextName.hashCode()) { case -1294635157: if (nextName.equals("errors")) { c2 = 0; break; } break; case -1243020381: if (nextName.equals("global")) { c2 = 1; break; } break; case -930157179: if (nextName.equals("retry_after")) { c2 = 2; break; } break; case 3059181: if (nextName.equals(ModelAuditLogEntry.CHANGE_KEY_CODE)) { c2 = 3; break; } break; case 954925063: if (nextName.equals("message")) { c2 = 4; break; } break; } switch (c2) { case 0: SkemaError skemaError = (SkemaError) jsonReader.parse(new SkemaError(null)); this.skemaError = skemaError; if (!SkemaError.access$400(skemaError).isEmpty()) { ArrayList arrayList = new ArrayList(); for (SkemaErrorItem skemaErrorItem : SkemaError.access$400(this.skemaError)) { arrayList.add(SkemaErrorItem.access$500(skemaErrorItem)); } this.messages.put("_misc", arrayList); } for (Map.Entry entry : SkemaError.access$600(this.skemaError).entrySet()) { ArrayList arrayList2 = new ArrayList(); String str = (String) entry.getKey(); SkemaError skemaError2 = (SkemaError) entry.getValue(); if (!SkemaError.access$400(skemaError2).isEmpty()) { for (SkemaErrorItem skemaErrorItem2 : SkemaError.access$400(skemaError2)) { arrayList2.add(SkemaErrorItem.access$500(skemaErrorItem2)); } } arrayList2.addAll(SkemaError.access$600(skemaError2).keySet()); this.messages.put(str, arrayList2); } return; case 1: this.global = jsonReader.nextBoolean(this.global); return; case 2: this.retryAfter = jsonReader.nextDouble(this.retryAfter); return; case 3: this.code = jsonReader.nextInt(this.code); return; case 4: this.message = jsonReader.nextString(this.message); return; default: this.messages.put(nextName, jsonReader.nextList(new a(jsonReader))); return; } } public boolean canEqual(Object obj) { return obj instanceof Response; } public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Response)) { return false; } Response response = (Response) obj; if (!response.canEqual(this) || getCode() != response.getCode() || Double.compare(this.retryAfter, response.retryAfter) != 0 || this.global != response.global) { return false; } SkemaError skemaError = this.skemaError; SkemaError skemaError2 = response.skemaError; if (skemaError != null ? !skemaError.equals(skemaError2) : skemaError2 != null) { return false; } Map> messages = getMessages(); Map> messages2 = response.getMessages(); if (messages != null ? !messages.equals(messages2) : messages2 != null) { return false; } String message = getMessage(); String message2 = response.getMessage(); return message != null ? message.equals(message2) : message2 == null; } public int getCode() { return this.code; } @Nullable public String getMessage() { return this.message; } @NonNull public Map> getMessages() { return this.messages; } @Nullable public Long getRetryAfterMs() { double d = this.retryAfter; if (d > ShadowDrawableWrapper.COS_45) { return Long.valueOf((long) (d * 1000.0d)); } return null; } public int hashCode() { long doubleToLongBits = Double.doubleToLongBits(this.retryAfter); int code = ((((getCode() + 59) * 59) + ((int) (doubleToLongBits ^ (doubleToLongBits >>> 32)))) * 59) + (this.global ? 79 : 97); SkemaError skemaError = this.skemaError; int i = 43; int hashCode = (code * 59) + (skemaError == null ? 43 : skemaError.hashCode()); Map> messages = getMessages(); int hashCode2 = (hashCode * 59) + (messages == null ? 43 : messages.hashCode()); String message = getMessage(); int i2 = hashCode2 * 59; if (message != null) { i = message.hashCode(); } return i2 + i; } public boolean isKnownResponse() { return this.code > 0 || !this.messages.isEmpty(); } public String toString() { StringBuilder R = c.d.b.a.a.R("Error.Response(code="); R.append(getCode()); R.append(", retryAfter="); R.append(this.retryAfter); R.append(", global="); R.append(this.global); R.append(", skemaError="); R.append(this.skemaError); R.append(", messages="); R.append(getMessages()); R.append(", message="); R.append(getMessage()); R.append(")"); return R.toString(); } } public static class SkemaError implements Model { @NonNull private List errors; @NonNull private Map subErrors; private SkemaError() { this.errors = Collections.emptyList(); this.subErrors = new HashMap(); } public /* synthetic */ SkemaError(AnonymousClass1 r1) { this(); } public static /* synthetic */ List access$400(SkemaError skemaError) { return skemaError.errors; } public static /* synthetic */ Map access$600(SkemaError skemaError) { return skemaError.subErrors; } /* JADX DEBUG: Multi-variable search result rejected for r1v2, resolved type: java.util.Map */ /* JADX WARN: Multi-variable type inference failed */ @Override // com.discord.models.domain.Model public void assignField(Model.JsonReader jsonReader) throws IOException { String nextName = jsonReader.nextName(); if ("_errors".equals(nextName)) { this.errors = jsonReader.nextList(new b(jsonReader)); } else { this.subErrors.put(nextName, jsonReader.parse(new SkemaError())); } } public boolean canEqual(Object obj) { return obj instanceof SkemaError; } public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SkemaError)) { return false; } SkemaError skemaError = (SkemaError) obj; if (!skemaError.canEqual(this)) { return false; } List list = this.errors; List list2 = skemaError.errors; if (list != null ? !list.equals(list2) : list2 != null) { return false; } Map map = this.subErrors; Map map2 = skemaError.subErrors; return map != null ? map.equals(map2) : map2 == null; } public int hashCode() { List list = this.errors; int i = 43; int hashCode = list == null ? 43 : list.hashCode(); Map map = this.subErrors; int i2 = (hashCode + 59) * 59; if (map != null) { i = map.hashCode(); } return i2 + i; } public String toString() { StringBuilder R = c.d.b.a.a.R("Error.SkemaError(errors="); R.append(this.errors); R.append(", subErrors="); return c.d.b.a.a.K(R, this.subErrors, ")"); } } public static class SkemaErrorItem implements Model { private String code; private String message; private SkemaErrorItem() { } public /* synthetic */ SkemaErrorItem(AnonymousClass1 r1) { this(); } public static /* synthetic */ String access$500(SkemaErrorItem skemaErrorItem) { return skemaErrorItem.message; } @Override // com.discord.models.domain.Model public void assignField(Model.JsonReader jsonReader) throws IOException { String nextName = jsonReader.nextName(); nextName.hashCode(); if (nextName.equals(ModelAuditLogEntry.CHANGE_KEY_CODE)) { this.code = jsonReader.nextString(null); } else if (!nextName.equals("message")) { jsonReader.skipValue(); } else { this.message = jsonReader.nextString(null); } } public boolean canEqual(Object obj) { return obj instanceof SkemaErrorItem; } public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SkemaErrorItem)) { return false; } SkemaErrorItem skemaErrorItem = (SkemaErrorItem) obj; if (!skemaErrorItem.canEqual(this)) { return false; } String str = this.code; String str2 = skemaErrorItem.code; if (str != null ? !str.equals(str2) : str2 != null) { return false; } String str3 = this.message; String str4 = skemaErrorItem.message; return str3 != null ? str3.equals(str4) : str4 == null; } public int hashCode() { String str = this.code; int i = 43; int hashCode = str == null ? 43 : str.hashCode(); String str2 = this.message; int i2 = (hashCode + 59) * 59; if (str2 != null) { i = str2.hashCode(); } return i2 + i; } public String toString() { StringBuilder R = c.d.b.a.a.R("Error.SkemaErrorItem(code="); R.append(this.code); R.append(", message="); return c.d.b.a.a.H(R, this.message, ")"); } } public enum Type { FORBIDDEN_CLOUD_FLARE, FORBIDDEN_DISCORD, INTERMITTENT_CLOUD_FLARE, DISCORD_REQUEST_ERROR, DISCORD_REQUEST_RESOURCE_NOT_FOUND, DISCORD_BAD_REQUEST, INTERNAL_SERVER_ERROR, REQUEST_TOO_LARGE, UNAUTHORIZED, DISCORD_REQUEST_ERROR_UNKNOWN, RATE_LIMITED, NETWORK, SSL, TIMEOUT, CAPTCHA_KNOWN_FAILURE, IMAGE_NOT_FOUND, OTHER } public Error(@NonNull Throwable th, @NonNull Type type, @NonNull Response response, @Nullable Map map, @Nullable String str) { Objects.requireNonNull(th, "throwable is marked non-null but is null"); Objects.requireNonNull(type, "type is marked non-null but is null"); Objects.requireNonNull(response, "response is marked non-null but is null"); this.throwable = th; this.type = type; this.response = response; this.metadata = map; this.bodyText = str; } /* JADX INFO: finally extract failed */ public static Error create(@NonNull Throwable th) { String str; Map map; Type type; Response response; Type type2; String str2; String str3; Headers headers; String str4; Map map2; Type type3; String str5 = ""; if (th instanceof HttpException) { HttpException httpException = (HttpException) th; int a = httpException.a(); retrofit2.Response response2 = httpException.i; if (response2 != null) { headers = response2.a.n; ResponseBody responseBody = response2.f2980c; if (responseBody != null) { MediaType b = responseBody.b(); str2 = b != null ? b.d : str5; if (b != null) { str5 = b.f; } try { str3 = responseBody.d(); responseBody.close(); } catch (IOException unused) { responseBody.close(); str3 = null; } catch (Throwable th2) { responseBody.close(); throw th2; } } else { str3 = null; str2 = null; } str4 = getRequestUrl(response2.a); } else { str4 = null; headers = null; str3 = null; str2 = null; } if (a == 500) { type3 = Type.INTERNAL_SERVER_ERROR; } else if (a == 502 || a == 503 || a == 520 || a == 521 || a == 522 || a == 525) { type3 = Type.INTERMITTENT_CLOUD_FLARE; } else if (a == 401) { type3 = Type.UNAUTHORIZED; } else if (a == 403 && str5.contains("html")) { type3 = Type.FORBIDDEN_CLOUD_FLARE; } else if (a == 413) { type3 = Type.REQUEST_TOO_LARGE; } else { Response response3 = new Response(str3, null); if (a == 400) { type3 = Type.DISCORD_BAD_REQUEST; } else if (a == 403) { type3 = Type.FORBIDDEN_DISCORD; } else if (response3.isKnownResponse()) { type3 = Type.DISCORD_REQUEST_ERROR; } else if (a == 404) { type3 = Type.DISCORD_REQUEST_RESOURCE_NOT_FOUND; } else if (a == 429) { type3 = Type.RATE_LIMITED; } else { Type type4 = Type.DISCORD_REQUEST_ERROR_UNKNOWN; Map metaData = getMetaData(str4, a, str2, headers); response = response3; map2 = metaData; type3 = type4; type = type3; map = map2; str = str3; } response = response3; map2 = null; type = type3; map = map2; str = str3; } map2 = null; response = null; type = type3; map = map2; str = str3; } else { if (th instanceof UnknownHostException) { type2 = Type.NETWORK; } else if (th instanceof IOException) { if (th.getMessage() != null) { str5 = th.getMessage(); } type2 = (str5.contains("Canceled") || str5.contains("Connection reset by peer") || str5.contains("stream was reset:") || (th instanceof NoRouteToHostException) || (th instanceof SocketException) || (th instanceof InterruptedIOException) || (th.getCause() != null && (th.getCause() instanceof InterruptedException)) || (th.getCause() instanceof SocketTimeoutException)) ? Type.NETWORK : ((th instanceof SSLHandshakeException) || (th instanceof SSLException)) ? Type.SSL : Type.OTHER; } else { type2 = th instanceof TimeoutException ? Type.TIMEOUT : th instanceof CaptchaHelper.Failure ? Type.CAPTCHA_KNOWN_FAILURE : th instanceof MGImagesBitmap.ImageNotFoundException ? Type.IMAGE_NOT_FOUND : Type.OTHER; } type = type2; response = null; map = null; str = null; } return new Error(th, type, response == null ? new Response(null, null) : response, map, str); } private static Map getMetaData(String str, int i, String str2, Headers headers) { HashMap hashMap = new HashMap(); hashMap.put("responseCode", String.valueOf(i)); hashMap.put("requestUrl", str); hashMap.put("content-type", str2); hashMap.put("CF-Ray", headers != null ? headers.c("CF-Ray") : null); return hashMap; } @Nullable private static String getRequestUrl(@Nullable okhttp3.Response response) { if (response == null) { return null; } return response.i.b.l; } @NonNull private List getToastMessages(@NonNull Context context) { switch (this.type.ordinal()) { case 0: return Collections.singletonList(context.getString(R.string.network_error_cloudflare_unauthorized)); case 1: return Collections.singletonList(Response.access$100(this.response, context, R.string.network_error_forbidden)); case 2: return Collections.singletonList(context.getString(R.string.network_error_cloudflare_intermittent)); case 3: case 9: return Collections.singletonList(Response.access$100(this.response, context, R.string.network_error_rest_request)); case 4: default: return Collections.singletonList(context.getString(R.string.network_error_unknown)); case 5: return Collections.singletonList(Response.access$100(this.response, context, R.string.network_error_bad_request)); case 6: return Collections.singletonList(context.getString(R.string.internal_server_error)); case 7: return Collections.singletonList(context.getString(R.string.network_error_request_too_large)); case 8: return Collections.singletonList(context.getString(R.string.network_error_unauthorized)); case 10: return Collections.singletonList(context.getString(R.string.rate_limited)); case 11: return Collections.singletonList(context.getString(R.string.network_error_connection)); case 12: return Collections.singletonList(context.getString(R.string.network_error_ssl)); case 13: return Collections.singletonList(context.getString(R.string.timeout_error)); } } public static void handle(@NonNull Throwable th, @NonNull String str, @Nullable Action1 action1, @Nullable Context context) throws Throwable { handle(th, str, action1, context, Boolean.FALSE); } public static void handle(@NonNull Throwable th, @NonNull String str, @Nullable Action1 action1, @Nullable Context context, @Nullable Boolean bool) throws Throwable { Error create = create(th); create.getType(); Type type = Type.OTHER; if (action1 != null) { try { action1.call(create); } catch (Exception e) { StringBuilder V = c.d.b.a.a.V(str, " / "); V.append(create.toString()); onUnhandledError.call(str, new Exception(V.toString(), e), null); return; } } create.logError(str); if (bool == null || !bool.booleanValue()) { create.showToasts(context); } } public static void init(@NonNull Action3> action3) { onUnhandledError = action3; } private void logError(String str) { Action3> action3; if (this.shouldLog.get()) { int ordinal = this.type.ordinal(); if (ordinal == 9) { Action3> action32 = onUnhandledError; if (action32 != null) { action32.call(this.type.name(), this.throwable, this.metadata); } } else if (ordinal == 16 && (action3 = onUnhandledError) != null) { action3.call(str, this.throwable, this.metadata); } } } @VisibleForTesting public static void setUnexpectedExceptionsCrashDebug(boolean z2) { unexpectedExceptionsCrashDebug = z2; } public boolean canEqual(Object obj) { return obj instanceof Error; } public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Error)) { return false; } Error error = (Error) obj; if (!error.canEqual(this)) { return false; } AtomicBoolean atomicBoolean = this.showErrorToasts; AtomicBoolean atomicBoolean2 = error.showErrorToasts; if (atomicBoolean != null ? !atomicBoolean.equals(atomicBoolean2) : atomicBoolean2 != null) { return false; } AtomicBoolean atomicBoolean3 = this.shouldLog; AtomicBoolean atomicBoolean4 = error.shouldLog; if (atomicBoolean3 != null ? !atomicBoolean3.equals(atomicBoolean4) : atomicBoolean4 != null) { return false; } Throwable throwable = getThrowable(); Throwable throwable2 = error.getThrowable(); if (throwable != null ? !throwable.equals(throwable2) : throwable2 != null) { return false; } Type type = getType(); Type type2 = error.getType(); if (type != null ? !type.equals(type2) : type2 != null) { return false; } Response response = getResponse(); Response response2 = error.getResponse(); if (response != null ? !response.equals(response2) : response2 != null) { return false; } Map map = this.metadata; Map map2 = error.metadata; if (map != null ? !map.equals(map2) : map2 != null) { return false; } String bodyText = getBodyText(); String bodyText2 = error.getBodyText(); return bodyText != null ? bodyText.equals(bodyText2) : bodyText2 == null; } @Nullable public String getBodyText() { return this.bodyText; } @NonNull public Response getResponse() { return this.response; } @NonNull public Throwable getThrowable() { return this.throwable; } @NonNull public Type getType() { return this.type; } public int hashCode() { AtomicBoolean atomicBoolean = this.showErrorToasts; int i = 43; int hashCode = atomicBoolean == null ? 43 : atomicBoolean.hashCode(); AtomicBoolean atomicBoolean2 = this.shouldLog; int hashCode2 = ((hashCode + 59) * 59) + (atomicBoolean2 == null ? 43 : atomicBoolean2.hashCode()); Throwable throwable = getThrowable(); int hashCode3 = (hashCode2 * 59) + (throwable == null ? 43 : throwable.hashCode()); Type type = getType(); int hashCode4 = (hashCode3 * 59) + (type == null ? 43 : type.hashCode()); Response response = getResponse(); int hashCode5 = (hashCode4 * 59) + (response == null ? 43 : response.hashCode()); Map map = this.metadata; int hashCode6 = (hashCode5 * 59) + (map == null ? 43 : map.hashCode()); String bodyText = getBodyText(); int i2 = hashCode6 * 59; if (bodyText != null) { i = bodyText.hashCode(); } return i2 + i; } public void setShouldLog(boolean z2) { this.shouldLog.set(z2); } public void setShowErrorToasts(boolean z2) { this.showErrorToasts.set(z2); } @MainThread public void showToasts(@Nullable Context context) { if (this.showErrorToasts.get() && context != null) { for (String str : getToastMessages(context)) { Toast.makeText(context, str, 0).show(); } } } public String toString() { StringBuilder R = c.d.b.a.a.R("Error(showErrorToasts="); R.append(this.showErrorToasts); R.append(", shouldLog="); R.append(this.shouldLog); R.append(", throwable="); R.append(getThrowable()); R.append(", type="); R.append(getType()); R.append(", response="); R.append(getResponse()); R.append(", metadata="); R.append(this.metadata); R.append(", bodyText="); R.append(getBodyText()); R.append(")"); return R.toString(); } }