1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.android_webview;
6
7import android.content.res.Resources;
8import android.util.SparseArray;
9
10import org.chromium.base.CalledByNative;
11import org.chromium.base.JNINamespace;
12
13import java.io.IOException;
14import java.io.InputStreamReader;
15import java.lang.ref.SoftReference;
16import java.util.NoSuchElementException;
17import java.util.Scanner;
18
19/**
20 * A class that defines a set of resource IDs and functionality to resolve
21 * those IDs to concrete resources.
22 */
23@JNINamespace("android_webview::AwResource")
24public class AwResource {
25    // The following resource ID's must be initialized by the embedder.
26
27    // Raw resource ID for an HTML page to be displayed in the case of
28    // a specific load error.
29    private static int sRawLoadError;
30
31    // Raw resource ID for an HTML page to be displayed in the case of
32    // a generic load error. (It's called NO_DOMAIN for legacy reasons).
33    private static int sRawNoDomain;
34
35    // Array resource ID for the configuration of platform specific key-systems.
36    private static int sStringArrayConfigKeySystemUUIDMapping;
37
38    // The embedder should inject a Resources object that will be used
39    // to resolve Resource IDs into the actual resources.
40    private static Resources sResources;
41
42    // Loading some resources is expensive, so cache the results.
43    private static SparseArray<SoftReference<String>> sResourceCache;
44
45    private static final int TYPE_STRING = 0;
46    private static final int TYPE_RAW = 1;
47
48    public static void setResources(Resources resources) {
49        sResources = resources;
50        sResourceCache = new SparseArray<SoftReference<String>>();
51    }
52
53    public static void setErrorPageResources(int loaderror, int nodomain) {
54        sRawLoadError = loaderror;
55        sRawNoDomain = nodomain;
56    }
57
58    public static void setConfigKeySystemUuidMapping(int config) {
59        sStringArrayConfigKeySystemUUIDMapping = config;
60    }
61
62    @CalledByNative
63    public static String getNoDomainPageContent() {
64        return getResource(sRawNoDomain, TYPE_RAW);
65    }
66
67    @CalledByNative
68    public static String getLoadErrorPageContent() {
69        return getResource(sRawLoadError, TYPE_RAW);
70    }
71
72    public static String[] getConfigKeySystemUuidMapping() {
73        // No need to cache, since this should be called only once.
74        return sResources.getStringArray(sStringArrayConfigKeySystemUUIDMapping);
75    }
76
77    private static String getResource(int resid, int type) {
78        assert resid != 0;
79        assert sResources != null;
80        assert sResourceCache != null;
81
82        SoftReference<String> stringRef = sResourceCache.get(resid);
83        String result = stringRef == null ? null : stringRef.get();
84        if (result == null) {
85            switch (type) {
86                case TYPE_STRING:
87                    result = sResources.getString(resid);
88                    break;
89                case TYPE_RAW:
90                    result = getRawFileResourceContent(resid);
91                    break;
92                default:
93                    throw new IllegalArgumentException("Unknown resource type");
94            }
95
96            sResourceCache.put(resid, new SoftReference<String>(result));
97        }
98        return result;
99    }
100
101    private static String getRawFileResourceContent(int resid) {
102        assert resid != 0;
103        assert sResources != null;
104
105        InputStreamReader isr = null;
106        String result = null;
107
108        try {
109            isr = new InputStreamReader(
110                    sResources.openRawResource(resid));
111            // \A tells the scanner to use the beginning of the input
112            // as the delimiter, hence causes it to read the entire text.
113            result = new Scanner(isr).useDelimiter("\\A").next();
114        } catch (Resources.NotFoundException e) {
115            return "";
116        } catch (NoSuchElementException e) {
117            return "";
118        } finally {
119            try {
120                if (isr != null) {
121                    isr.close();
122                }
123            } catch (IOException e) {
124                // Nothing to do if close() fails.
125            }
126        }
127        return result;
128    }
129}
130