1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.inputmethod.keyboard.tools;
18 
19 import org.xml.sax.Attributes;
20 import org.xml.sax.SAXException;
21 import org.xml.sax.SAXParseException;
22 import org.xml.sax.ext.DefaultHandler2;
23 
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 
33 import javax.xml.parsers.ParserConfigurationException;
34 import javax.xml.parsers.SAXParser;
35 import javax.xml.parsers.SAXParserFactory;
36 
37 public class StringResourceMap {
38     // Locale of this string resource map.
39     public final Locale mLocale;
40     // String resource list.
41     private final List<StringResource> mResources;
42     // Name to string resource map.
43     private final Map<String, StringResource> mResourcesMap;
44 
45     // The length of String[] that is created from this {@link StringResourceMap}. The length is
46     // calculated in {@link MoreKeysResources#dumpTexts(OutputStream)} and recorded by
47     // {@link #setOutputArraySize(int)}. The recorded length is used as a part of comment by
48     // {@link MoreKeysResources#dumpLocaleMap(OutputStream)} via {@link #getOutputArraySize()}.
49     private int mOutputArraySize;
50 
StringResourceMap(final String jarEntryName)51     public StringResourceMap(final String jarEntryName) {
52         mLocale = JarUtils.getLocaleFromEntryName(jarEntryName);
53         final StringResourceHandler handler = new StringResourceHandler();
54         final SAXParserFactory factory = SAXParserFactory.newInstance();
55         factory.setNamespaceAware(true);
56         final InputStream stream = JarUtils.openResource(jarEntryName);
57         try {
58             final SAXParser parser = factory.newSAXParser();
59             // In order to get comment tag.
60             parser.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
61             parser.parse(stream, handler);
62         } catch (ParserConfigurationException e) {
63             throw new RuntimeException(e.getMessage(), e);
64         } catch (SAXParseException e) {
65             throw new RuntimeException(e.getMessage() + " at line " + e.getLineNumber()
66                     + ", column " + e.getColumnNumber(), e);
67         } catch (SAXException e) {
68             throw new RuntimeException(e.getMessage(), e);
69         } catch (IOException e) {
70             throw new RuntimeException(e.getMessage(), e);
71         } finally {
72             JarUtils.close(stream);
73         }
74 
75         mResources = Collections.unmodifiableList(handler.mResources);
76         final HashMap<String, StringResource> map = new HashMap<>();
77         for (final StringResource res : mResources) {
78             map.put(res.mName, res);
79         }
80         mResourcesMap = map;
81     }
82 
getResources()83     public List<StringResource> getResources() {
84         return mResources;
85     }
86 
contains(final String name)87     public boolean contains(final String name) {
88         return mResourcesMap.containsKey(name);
89     }
90 
get(final String name)91     public StringResource get(final String name) {
92         return mResourcesMap.get(name);
93     }
94 
setOutputArraySize(final int arraySize)95     public void setOutputArraySize(final int arraySize) {
96         mOutputArraySize = arraySize;
97     }
98 
getOutputArraySize()99     public int getOutputArraySize() {
100         return mOutputArraySize;
101     }
102 
103     static class StringResourceHandler extends DefaultHandler2 {
104         private static final String TAG_RESOURCES = "resources";
105         private static final String TAG_STRING = "string";
106         private static final String ATTR_NAME = "name";
107 
108         final ArrayList<StringResource> mResources = new ArrayList<>();
109 
110         private String mName;
111         private final StringBuilder mValue = new StringBuilder();
112         private final StringBuilder mComment = new StringBuilder();
113 
init()114         private void init() {
115             mName = null;
116             mComment.setLength(0);
117         }
118 
119         @Override
comment(char[] ch, int start, int length)120         public void comment(char[] ch, int start, int length) {
121             mComment.append(ch, start, length);
122             if (ch[start + length - 1] != '\n') {
123                 mComment.append('\n');
124             }
125         }
126 
127         @Override
startElement(String uri, String localName, String qName, Attributes attr)128         public void startElement(String uri, String localName, String qName, Attributes attr) {
129             if (TAG_RESOURCES.equals(localName)) {
130                 init();
131             } else if (TAG_STRING.equals(localName)) {
132                 mName = attr.getValue(ATTR_NAME);
133                 mValue.setLength(0);
134             }
135         }
136 
137         @Override
characters(char[] ch, int start, int length)138         public void characters(char[] ch, int start, int length) {
139             mValue.append(ch, start, length);
140         }
141 
142         @Override
endElement(String uri, String localName, String qName)143         public void endElement(String uri, String localName, String qName) throws SAXException {
144             if (TAG_STRING.equals(localName)) {
145                 if (mName == null)
146                     throw new SAXException(TAG_STRING + " doesn't have name");
147                 final String comment = mComment.length() > 0 ? mComment.toString() : null;
148                 String value = mValue.toString();
149                 if (value.startsWith("\"") && value.endsWith("\"")) {
150                     // Trim surroundings double quote.
151                     value = value.substring(1, value.length() - 1);
152                 }
153                 mResources.add(new StringResource(mName, value, comment));
154                 init();
155             }
156         }
157     }
158 }
159