package org.wikipedia.json;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.gson.reflect.TypeToken;

import org.wikipedia.crash.RemoteLogException;
import org.wikipedia.page.tabs.Tab;
import org.wikipedia.util.log.L;

import java.util.Collections;
import java.util.List;

public final class TabUnmarshaller {
    private static final TypeToken<List<Tab>> TYPE_TOKEN = new TypeToken<List<Tab>>() { };

    @NonNull public static List<Tab> unmarshal(@Nullable String json) {
        List<Tab> object = null;
        try {
            object = GsonUnmarshaller.unmarshal(TYPE_TOKEN, json);
        } catch (Exception e) {
            // Catch all. Any Exception can be thrown when unmarshalling.
            L.logRemoteErrorIfProd(new RemoteLogException(e).put("json", json));
        }
        if (object == null) {
            object = Collections.emptyList();
        }
        return object;
    }

    private TabUnmarshaller() { }
}

package org.wikipedia.json;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.google.gson.reflect.TypeToken;

import org.wikipedia.crash.RemoteLogException;
import org.wikipedia.page.PageBackStackItem;
import org.wikipedia.page.tabs.Tab;
import org.wikipedia.util.log.L;

import java.util.Collections;
import java.util.List;

public final class TabUnmarshaller {
    private static final TypeToken<List<Tab>> TYPE_TOKEN = new TypeToken<List<Tab>>() { };

    @NonNull public static List<Tab> unmarshal(@Nullable String json) {
        List<Tab> object = null;
        try {
            object = GsonUnmarshaller.unmarshal(TYPE_TOKEN, json);
        } catch (Exception e) {
            // Catch all. Any Exception can be thrown when unmarshalling.
            L.logRemoteErrorIfProd(new RemoteLogException(e).put("json", json));
        }
        if (object == null) {
            object = Collections.emptyList();
        }

        // T152980
        // When upgrading from much older versions (namely, 2.1.141 or earlier), the serialized
        // tab list may be in a format that causes WikiSite objects to have null Uri components.
        // If we encounter one of these occurrences, just clear out the entire tab list.
        // to be on the safe side.
        // TODO: Remove when the incidence of this is sufficiently diminished (April 2017?)
        boolean bad = false;
        for (Tab tab : object) {
            for (PageBackStackItem item : tab.getBackStack()) {
                if (TextUtils.isEmpty(item.getTitle().getWikiSite().authority())
                        || TextUtils.isEmpty(item.getHistoryEntry().getTitle().getWikiSite().authority())) {
                    L.logRemoteErrorIfProd(new IllegalArgumentException("Format error in serialized tab list."));
                    bad = true;
                    break;
                }
            }
        }
        if (bad) {
            object.clear();
        }

        return object;
    }

    private TabUnmarshaller() { }
}

Fix possible crash when upgrading from older app versions.
Fix possible crash when upgrading from older app versions.

In versions of the app prior to 2.1.142, the list of Tabs was serialized
in a way that's not completely consistent with the way it's serialized
today. Specifically, the "domain" item of the "site" component can
sometimes be an empty string, which would manifest itself as a Uri with
an empty authority.

This leads to a dangerous condition where the tab list is deserialized
"successfully", but the Uri contains a ticking time bomb of null members,
which causes a confusing exception when the Uri is passed to Retrofit.

This patch adds logic to look for tab entries that match this description
after deserializing, and if found, fall back to clearing out the tab list
and starting fresh.

Bug: T152980
Change-Id: If09f6832e6a4f3e2b47a3806cb4e8da9cad8031e
