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 }