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