    001    // Copyright 2010 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.internal.services.messages;
    016
    017    import java.io.ByteArrayInputStream;
    018    import java.io.IOException;
    019    import java.io.InputStream;
    020    import java.io.InputStreamReader;
    021    import java.io.Reader;
    022    import java.util.Map;
    023    import java.util.Properties;
    024
    025    import org.apache.tapestry5.ioc.Resource;
    026    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    027    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    028    import org.apache.tapestry5.services.messages.PropertiesFileParser;
    029
    030    public class PropertiesFileParserImpl implements PropertiesFileParser
    031    {
    032        /**
    033         * Charset used when reading a properties file.
    034         */
    035        private static final String CHARSET = "UTF-8";
    036
    037        /**
    038         * Buffer size used when reading a properties file.
    039         */
    040        private static final int BUFFER_SIZE = 2000;
    041
    042        public Map<String, String> parsePropertiesFile(Resource resource) throws IOException
    043        {
    044            Map<String, String> result = CollectionFactory.newCaseInsensitiveMap();
    045
    046            Properties p = new Properties();
    047            InputStream is = null;
    048
    049            try
    050            {
    051
    052                is = readUTFStreamToEscapedASCII(resource.openStream());
    053
    054                // Ok, now we have the content read into memory as UTF-8, not ASCII.
    055
    056                p.load(is);
    057
    058                is.close();
    059
    060                is = null;
    061            }
    062            finally
    063            {
    064                InternalUtils.close(is);
    065            }
    066
    067            for (Map.Entry e : p.entrySet())
    068            {
    069                String key = e.getKey().toString();
    070
    071                String value = p.getProperty(key);
    072
    073                result.put(key, value);
    074            }
    075
    076            return result;
    077        }
    078
    079        /**
    080         * Reads a UTF-8 stream, performing a conversion to ASCII (i.e., ISO8859-1 encoding). Characters outside the normal
    081         * range for ISO8859-1 are converted to unicode escapes. In effect, Tapestry is performing native2ascii on the
    082         * files, on the fly.
    083         */
    084        private static InputStream readUTFStreamToEscapedASCII(InputStream is) throws IOException
    085        {
    086            Reader reader = new InputStreamReader(is, CHARSET);
    087
    088            StringBuilder builder = new StringBuilder(BUFFER_SIZE);
    089            char[] buffer = new char[BUFFER_SIZE];
    090
    091            while (true)
    092            {
    093                int length = reader.read(buffer);
    094
    095                if (length < 0)
    096                    break;
    097
    098                for (int i = 0; i < length; i++)
    099                {
    100                    char ch = buffer[i];
    101
    102                    if (ch <= '\u007f')
    103                    {
    104                        builder.append(ch);
    105                        continue;
    106                    }
    107
    108                    builder.append(String.format("\\u%04x", (int) ch));
    109                }
    110            }
    111
    112            reader.close();
    113
    114            byte[] resourceContent = builder.toString().getBytes();
    115
    116            return new ByteArrayInputStream(resourceContent);
    117        }
    118    }