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
176bfd5f631908c4afd893c9b25b353e5e16c5fc0cTadashi G. Takaokapackage com.android.inputmethod.latin.maketext;
182be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
192be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport org.xml.sax.Attributes;
202be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport org.xml.sax.SAXException;
212be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport org.xml.sax.ext.DefaultHandler2;
222be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
232be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.IOException;
242be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.io.InputStream;
252be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.ArrayList;
262be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.Collections;
272be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.HashMap;
282be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.List;
292be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport java.util.Map;
302be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
312be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport javax.xml.parsers.ParserConfigurationException;
322be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport javax.xml.parsers.SAXParser;
332be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokaimport javax.xml.parsers.SAXParserFactory;
342be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
352be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaokapublic class StringResourceMap {
362be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    // String resource list.
372be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private final List<StringResource> mResources;
382be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    // Name to string resource map.
392be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    private final Map<String, StringResource> mResourcesMap;
402be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
412be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    public StringResourceMap(final InputStream is) {
422be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final StringResourceHandler handler = new StringResourceHandler();
432be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final SAXParserFactory factory = SAXParserFactory.newInstance();
442be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        factory.setNamespaceAware(true);
452be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        try {
462be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            final SAXParser parser = factory.newSAXParser();
472be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            // In order to get comment tag.
482be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            parser.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
492be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            parser.parse(is, handler);
502be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        } catch (ParserConfigurationException e) {
512be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        } catch (SAXException e) {
522be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            throw new RuntimeException(e.getMessage());
532be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        } catch (IOException e) {
542be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
552be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
562be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        mResources = Collections.unmodifiableList(handler.mResources);
572be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final HashMap<String,StringResource> map = new HashMap<String,StringResource>();
582be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        for (final StringResource res : mResources) {
592be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            map.put(res.mName, res);
602be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
612be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        mResourcesMap = map;
622be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
632be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
642be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    public List<StringResource> getResources() {
652be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        return mResources;
662be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
672be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
682be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    public boolean contains(final String name) {
692be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        return mResourcesMap.containsKey(name);
702be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
712be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
722be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    public StringResource get(final String name) {
732be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        return mResourcesMap.get(name);
742be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
752be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
762be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    static class StringResourceHandler extends DefaultHandler2 {
772be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        private static final String TAG_RESOURCES = "resources";
782be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        private static final String TAG_STRING = "string";
792be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        private static final String ATTR_NAME = "name";
802be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
812be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        final ArrayList<StringResource> mResources = new ArrayList<StringResource>();
822be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
832be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        private String mName;
842be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        private final StringBuilder mValue = new StringBuilder();
852be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        private final StringBuilder mComment = new StringBuilder();
862be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
872be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        private void init() {
882be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            mName = null;
892be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            mComment.setLength(0);
902be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
912be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
922be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        @Override
932be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        public void comment(char[] ch, int start, int length) {
942be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            mComment.append(ch, start, length);
955e90f2873c92e14349bd781d69beff4f7b33e760Tadashi G. Takaoka            if (ch[start + length - 1] != '\n') {
965e90f2873c92e14349bd781d69beff4f7b33e760Tadashi G. Takaoka                mComment.append('\n');
975e90f2873c92e14349bd781d69beff4f7b33e760Tadashi G. Takaoka            }
982be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
992be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1002be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        @Override
1012be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        public void startElement(String uri, String localName, String qName, Attributes attr) {
1022be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            if (TAG_RESOURCES.equals(localName)) {
1032be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                init();
1042be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            } else if (TAG_STRING.equals(localName)) {
1052be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                mName = attr.getValue(ATTR_NAME);
1062be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                mValue.setLength(0);
1072be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            }
1082be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1092be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1102be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        @Override
1112be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        public void characters(char[] ch, int start, int length) {
1122be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            mValue.append(ch, start, length);
1132be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1142be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka
1152be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        @Override
1162be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        public void endElement(String uri, String localName, String qName) throws SAXException {
1172be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            if (TAG_STRING.equals(localName)) {
1182be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                if (mName == null)
1192be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                    throw new SAXException(TAG_STRING + " doesn't have name");
1202be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                final String comment = mComment.length() > 0 ? mComment.toString() : null;
1212be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                String value = mValue.toString();
1222be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                if (value.startsWith("\"") && value.endsWith("\"")) {
1232be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                    // Trim surroundings double quote.
1242be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                    value = value.substring(1, value.length() - 1);
1252be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                }
1262be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                mResources.add(new StringResource(mName, value, comment));
1272be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka                init();
1282be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka            }
1292be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka        }
1302be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka    }
1312be51f4fd0c5cd70c7a2757558ffe45e703700cfTadashi G. Takaoka}
132