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 }