12be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka/*
22be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * Copyright (C) 2012 The Android Open Source Project
32be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka *
42be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
52be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * you may not use this file except in compliance with the License.
62be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * You may obtain a copy of the License at
72be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka *
82be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
92be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka *
102be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
112be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
122be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * See the License for the specific language governing permissions and
142be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka * limitations under the License.
152be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka */
162be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
17ca6acfdd6b3400ad6e29d45c29b0ec40ea92a968Tadashi G. Takaokapackage com.android.inputmethod.keyboard.tools;
182be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
192be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.File;
202be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.IOException;
212be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.InputStreamReader;
222be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.LineNumberReader;
232be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.PrintStream;
242be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.ArrayList;
252be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.Collections;
260fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaokaimport java.util.Comparator;
272be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.HashMap;
28d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaokaimport java.util.Locale;
29465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaokaimport java.util.TreeMap;
302be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.jar.JarFile;
312be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
322be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokapublic class MoreKeysResources {
336bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka    private static final String TEXT_RESOURCE_NAME = "donottranslate-more-keys.xml";
342be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
35e5320c1d1fb31c570841f656941758e5f064b896Tadashi G. Takaoka    private static final String JAVA_TEMPLATE = "KeyboardTextsTable.tmpl";
362be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private static final String MARK_NAMES = "@NAMES@";
376bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka    private static final String MARK_DEFAULT_TEXTS = "@DEFAULT_TEXTS@";
386bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka    private static final String MARK_TEXTS = "@TEXTS@";
396bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka    private static final String TEXTS_ARRAY_NAME_PREFIX = "TEXTS_";
406bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka    private static final String MARK_LOCALES_AND_TEXTS = "@LOCALES_AND_TEXTS@";
412be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private static final String EMPTY_STRING_VAR = "EMPTY";
422be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
432be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private final JarFile mJar;
44465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka    // String resources maps sorted by its language. The language is determined from the jar entry
456bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka    // name by calling {@link JarUtils#getLocaleFromEntryName(String)}.
46a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private final TreeMap<String, StringResourceMap> mResourcesMap = new TreeMap<>();
470fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    // Default string resources map.
480fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    private final StringResourceMap mDefaultResourceMap;
490fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    // Histogram of string resource names. This is used to sort {@link #mSortedResourceNames}.
50a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private final HashMap<String, Integer> mNameHistogram = new HashMap<>();
510fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    // Sorted string resource names array; Descending order of histogram count.
520fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    // The string resource name is specified as an attribute "name" in string resource files.
530fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    // The string resource can be accessed by specifying name "!text/<name>"
540fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    // via {@link KeyboardTextsSet#getText(String)}.
550fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    private final String[] mSortedResourceNames;
562be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
572be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    public MoreKeysResources(final JarFile jar) {
582be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        mJar = jar;
59465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka        final ArrayList<String> resourceEntryNames = JarUtils.getEntryNameListing(
60465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka                jar, TEXT_RESOURCE_NAME);
61465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka        for (final String entryName : resourceEntryNames) {
62465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka            final StringResourceMap resMap = new StringResourceMap(entryName);
63d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka            mResourcesMap.put(LocaleUtils.getLocaleCode(resMap.mLocale), resMap);
642be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
65d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka        mDefaultResourceMap = mResourcesMap.get(
66d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka                LocaleUtils.getLocaleCode(LocaleUtils.DEFAULT_LOCALE));
670fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka
680fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        // Initialize name histogram and names list.
690fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        final HashMap<String, Integer> nameHistogram = mNameHistogram;
70a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        final ArrayList<String> resourceNamesList = new ArrayList<>();
710fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        for (final StringResource res : mDefaultResourceMap.getResources()) {
720fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            nameHistogram.put(res.mName, 0); // Initialize histogram value.
730fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            resourceNamesList.add(res.mName);
740fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        }
750fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        // Make name histogram.
766bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka        for (final String locale : mResourcesMap.keySet()) {
776bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka            final StringResourceMap resMap = mResourcesMap.get(locale);
780fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            if (resMap == mDefaultResourceMap) continue;
790fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            for (final StringResource res : resMap.getResources()) {
800fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                if (!mDefaultResourceMap.contains(res.mName)) {
816bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka                    throw new RuntimeException(res.mName + " in " + locale
820fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                            + " doesn't have default resource");
830fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                }
840fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                final int histogramValue = nameHistogram.get(res.mName);
850fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                nameHistogram.put(res.mName, histogramValue + 1);
860fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            }
870fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        }
880fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        // Sort names list.
890fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        Collections.sort(resourceNamesList, new Comparator<String>() {
900fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            @Override
910fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            public int compare(final String leftName, final String rightName) {
920fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                final int leftCount = nameHistogram.get(leftName);
930fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                final int rightCount = nameHistogram.get(rightName);
940fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                // Descending order of histogram count.
950fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                if (leftCount > rightCount) return -1;
960fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                if (leftCount < rightCount) return 1;
970fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                // TODO: Add further criteria to order the same histogram value names to be able to
980fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                // minimize footprints of string resources arrays.
990fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                return 0;
1000fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            }
1010fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        });
1020fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        mSortedResourceNames = resourceNamesList.toArray(new String[resourceNamesList.size()]);
1032be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
1042be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1052be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    public void writeToJava(final String outDir) {
106465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka        final ArrayList<String> list = JarUtils.getEntryNameListing(mJar, JAVA_TEMPLATE);
1072fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (list.isEmpty()) {
1082be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            throw new RuntimeException("Can't find java template " + JAVA_TEMPLATE);
1092fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
1102fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        if (list.size() > 1) {
1112be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            throw new RuntimeException("Found multiple java template " + JAVA_TEMPLATE);
1122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        }
1132be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final String template = list.get(0);
1142be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final String javaPackage = template.substring(0, template.lastIndexOf('/'));
1152be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        PrintStream ps = null;
1162be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        LineNumberReader lnr = null;
1172be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        try {
1182be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            if (outDir == null) {
1192be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                ps = System.out;
1202be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            } else {
1212be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                final File outPackage = new File(outDir, javaPackage);
1222be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                final File outputFile = new File(outPackage,
1232be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                        JAVA_TEMPLATE.replace(".tmpl", ".java"));
1242be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                outPackage.mkdirs();
1259c9802e6473e5e2e2b792d5f14502139feb49637Tadashi G. Takaoka                ps = new PrintStream(outputFile, "UTF-8");
1262be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            }
1272be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            lnr = new LineNumberReader(new InputStreamReader(JarUtils.openResource(template)));
1282be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            inflateTemplate(lnr, ps);
1292be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        } catch (IOException e) {
1302be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            throw new RuntimeException(e);
1312be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        } finally {
132465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka            JarUtils.close(lnr);
133465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka            JarUtils.close(ps);
1342be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1352be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
1362be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1372be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private void inflateTemplate(final LineNumberReader in, final PrintStream out)
1382be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            throws IOException {
1392be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        String line;
1402be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        while ((line = in.readLine()) != null) {
1412be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            if (line.contains(MARK_NAMES)) {
1422be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                dumpNames(out);
1436bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka            } else if (line.contains(MARK_DEFAULT_TEXTS)) {
1446bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka                dumpDefaultTexts(out);
1456bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka            } else if (line.contains(MARK_TEXTS)) {
1466bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka                dumpTexts(out);
1476bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka            } else if (line.contains(MARK_LOCALES_AND_TEXTS)) {
1486bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka                dumpLocalesMap(out);
1492be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            } else {
1502be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                out.println(line);
1512be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            }
1522be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1532be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
1542be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1552be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private void dumpNames(final PrintStream out) {
1560fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        final int namesCount = mSortedResourceNames.length;
1570fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        for (int index = 0; index < namesCount; index++) {
1580fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            final String name = mSortedResourceNames[index];
1590fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            final int histogramValue = mNameHistogram.get(name);
1600fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            out.format("        /* %3d:%2d */ \"%s\",\n", index, histogramValue, name);
1612be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1622be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
1632be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1646bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka    private void dumpDefaultTexts(final PrintStream out) {
1650fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        final int outputArraySize = dumpTextsInternal(out, mDefaultResourceMap);
1660fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        mDefaultResourceMap.setOutputArraySize(outputArraySize);
167ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka    }
168ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka
169d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka    private static String getArrayNameForLocale(final Locale locale) {
170d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka        return TEXTS_ARRAY_NAME_PREFIX + LocaleUtils.getLocaleCode(locale);
1712be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
1722be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1736bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaoka    private void dumpTexts(final PrintStream out) {
174465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka        for (final StringResourceMap resMap : mResourcesMap.values()) {
175d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka            final Locale locale = resMap.mLocale;
1760fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            if (resMap == mDefaultResourceMap) continue;
1776bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka            out.format("    /* Locale %s: %s */\n",
1786bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka                    locale, LocaleUtils.getLocaleDisplayName(locale));
1796bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka            out.format("    private static final String[] " + getArrayNameForLocale(locale)
180ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka                    + " = {\n");
1810fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            final int outputArraySize = dumpTextsInternal(out, resMap);
182ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka            resMap.setOutputArraySize(outputArraySize);
1832be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            out.format("    };\n\n");
1842be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1852be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
1862be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1876bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka    private void dumpLocalesMap(final PrintStream out) {
188465a2ac534bf5c0dea38608dbe713ed3a9dc84ebTadashi G. Takaoka        for (final StringResourceMap resMap : mResourcesMap.values()) {
189d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka            final Locale locale = resMap.mLocale;
190d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka            final String localeStr = LocaleUtils.getLocaleCode(locale);
191d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka            final String localeToDump = (locale == LocaleUtils.DEFAULT_LOCALE)
192d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka                    ? String.format("\"%s\"", localeStr)
193d317796207d9c9443669ff94aac63c4193ec0e6fTadashi G. Takaoka                    : String.format("\"%s\"%s", localeStr, "       ".substring(localeStr.length()));
1946bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka            out.format("        %s, %-12s /* %3d/%3d %s */\n",
1956bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka                    localeToDump, getArrayNameForLocale(locale) + ",",
196ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka                    resMap.getResources().size(), resMap.getOutputArraySize(),
1976bb3556ff7b24a5a38d7cc4276017bda3a9a4bbaTadashi G. Takaoka                    LocaleUtils.getLocaleDisplayName(locale));
1982be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1992be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
2002be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
2010fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka    private int dumpTextsInternal(final PrintStream out, final StringResourceMap resMap) {
2022be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final ArrayInitializerFormatter formatter =
2030fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                new ArrayInitializerFormatter(out, 100, "        ", mSortedResourceNames);
204ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka        int outputArraySize = 0;
2052be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        boolean successiveNull = false;
2060fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        final int namesCount = mSortedResourceNames.length;
2070fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka        for (int index = 0; index < namesCount; index++) {
2080fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            final String name = mSortedResourceNames[index];
2090fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            final StringResource res = resMap.get(name);
2100fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka            if (res != null) {
2110fe4d00068fb9ea85bfab083aa595082dd24e59cTadashi G. Takaoka                // TODO: Check whether the resource value is equal to the default.
2122be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                if (res.mComment != null) {
2132be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                    formatter.outCommentLines(addPrefix("        // ", res. mComment));
2142be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                }
2152be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                final String escaped = escapeNonAscii(res.mValue);
2162be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                if (escaped.length() == 0) {
2172be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                    formatter.outElement(EMPTY_STRING_VAR + ",");
2182be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                } else {
2192be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                    formatter.outElement(String.format("\"%s\",", escaped));
2202be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                }
2212be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                successiveNull = false;
222ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka                outputArraySize = formatter.getCurrentIndex();
2232be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            } else {
2242be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                formatter.outElement("null,");
2252be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                successiveNull = true;
2262be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            }
2272be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
2282be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        if (!successiveNull) {
2292be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            formatter.flush();
2302be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
231ed509695a42455874aa9c047ed4343f636ef527bTadashi G. Takaoka        return outputArraySize;
2322be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
2332be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
2342be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private static String addPrefix(final String prefix, final String lines) {
2352be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final StringBuilder sb = new StringBuilder();
2362be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        for (final String line : lines.split("\n")) {
2372be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            sb.append(prefix + line.trim() + "\n");
2382be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
2392be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        return sb.toString();
2402be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
2412be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
2422be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private static String escapeNonAscii(final String text) {
2432be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final StringBuilder sb = new StringBuilder();
2442be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final int length = text.length();
2452be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        for (int i = 0; i < length; i++) {
2462be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            final char c = text.charAt(i);
2472be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            if (c >= ' ' && c < 0x7f) {
2482be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                sb.append(c);
2492be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            } else {
2502be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                sb.append(String.format("\\u%04X", (int)c));
2512be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            }
2522be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
253e422c94634da4d0e61a9106addebf06cbcf70c2eTadashi G. Takaoka        return sb.toString();
2542be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
2552be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka}
256