Clover coverage report - Code Coverage for tapestry release 4.0-rc-2
Coverage timestamp: Sat Dec 17 2005 09:39:46 PST
file stats: LOC: 220   Methods: 8
NCLOC: 126   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
LocalizedPropertiesLoader.java 84.6% 86.1% 100% 86.8%
coverage coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.util.text;
 16   
 17    import java.io.BufferedReader;
 18    import java.io.IOException;
 19    import java.io.InputStream;
 20    import java.io.InputStreamReader;
 21    import java.io.Reader;
 22    import java.io.UnsupportedEncodingException;
 23    import java.util.Map;
 24   
 25    /**
 26    * An object that loads a properties file from the provided input stream or reader.
 27    * This class reads the property file exactly like java.util.Properties,
 28    * except that it also allows the files to use an encoding other than ISO-8859-1
 29    * and all non-ASCII characters are read correctly using the given encoding.
 30    * In short, non-latin characters no longer need to be quoted using native2ascii.
 31    *
 32    * @author mb
 33    * @since 4.0
 34    */
 35    public class LocalizedPropertiesLoader
 36    {
 37    private static final String HEX_DIGITS = "0123456789ABCDEF";
 38   
 39    private static final ICharacterMatcher WHITESPACE = new WhitespaceMatcher(false);
 40    private static final ICharacterMatcher LINE_SEPARATOR = new AsciiCharacterMatcher("\n\r");
 41    private static final ICharacterMatcher NOT_LINE_SEPARATOR = new InverseMatcher(LINE_SEPARATOR);
 42    private static final ICharacterMatcher KEY_VALUE_SEPARATOR = new AsciiCharacterMatcher("=:");
 43    private static final ICharacterMatcher SEPARATOR = new AsciiCharacterMatcher("=:\r\n");
 44    private static final ICharacterMatcher COMMENT = new AsciiCharacterMatcher("#!");
 45    private static final ICharacterMatcher WHITESPACE_OR_SEPARATOR =
 46    new CompoundMatcher(new ICharacterMatcher[] { WHITESPACE, SEPARATOR });
 47   
 48    private ExtendedReader _extendedReader;
 49   
 50    /**
 51    * Creates a new loader that will load the properties from the given input stream
 52    * using the default character encoding
 53    *
 54    * @param ins the input stream to load the properties from
 55    */
 56  78 public LocalizedPropertiesLoader(InputStream ins)
 57    {
 58  78 this(new InputStreamReader(ins));
 59    }
 60   
 61    /**
 62    * Creates a new loader that will load the properties from the given input stream
 63    * using the provided character encoding
 64    *
 65    * @param ins the input stream to load the properties from
 66    * @param encoding the character encoding the be used when reading from the stream
 67    * @throws UnsupportedEncodingException if the name of the encoding cannot be recognized
 68    */
 69  10 public LocalizedPropertiesLoader(InputStream ins, String encoding) throws UnsupportedEncodingException
 70    {
 71  10 this(new InputStreamReader(ins, encoding));
 72    }
 73   
 74    /**
 75    * Creates a new loader that will load the properties from the given reader
 76    *
 77    * @param reader the Reader to load the properties from
 78    */
 79  88 public LocalizedPropertiesLoader(Reader reader)
 80    {
 81  88 _extendedReader = new ExtendedReader(new BufferedReader(reader));
 82    }
 83   
 84    /**
 85    * Read the properties from the provided stream and store them into the given map
 86    *
 87    * @param properties the map where the properties will be stored
 88    * @throws IOException if an error occurs
 89    */
 90  88 public void load(Map properties) throws IOException
 91    {
 92  88 while (!isAtEndOfStream()) {
 93    // we are at the beginning of a line.
 94    // check whether it is a comment and if it is, skip it
 95  2543 int nextChar = _extendedReader.peek();
 96  2543 if (COMMENT.matches((char) nextChar)) {
 97  1146 _extendedReader.skipCharacters(NOT_LINE_SEPARATOR);
 98  1146 continue;
 99    }
 100   
 101  1397 _extendedReader.skipCharacters(WHITESPACE);
 102  1397 if (!isAtEndOfLine()) {
 103    // this line does not consist only of whitespace. the next word is the key
 104  250 String key = readQuotedLine(WHITESPACE_OR_SEPARATOR);
 105  250 _extendedReader.skipCharacters(WHITESPACE);
 106   
 107    // if the next char is a key-value separator, read it and skip the following spaces
 108  250 nextChar = _extendedReader.peek();
 109  250 if (nextChar > 0 && KEY_VALUE_SEPARATOR.matches((char) nextChar)) {
 110  242 _extendedReader.read();
 111  242 _extendedReader.skipCharacters(WHITESPACE);
 112    }
 113   
 114    // finally, read the value
 115  250 String value = readQuotedLine(LINE_SEPARATOR);
 116   
 117  248 properties.put(key, value);
 118    }
 119  1395 _extendedReader.skipCharacters(LINE_SEPARATOR);
 120    }
 121    }
 122   
 123   
 124  2629 private boolean isAtEndOfStream() throws IOException
 125    {
 126  2629 int nextChar = _extendedReader.peek();
 127  2629 return (nextChar < 0);
 128    }
 129   
 130   
 131  1397 private boolean isAtEndOfLine() throws IOException
 132    {
 133  1397 int nextChar = _extendedReader.peek();
 134  1397 if (nextChar < 0)
 135  0 return true;
 136  1397 return LINE_SEPARATOR.matches((char) nextChar);
 137    }
 138   
 139   
 140  500 private String readQuotedLine(ICharacterMatcher terminators) throws IOException
 141    {
 142  500 StringBuffer buf = new StringBuffer();
 143   
 144  500 while (true) {
 145    // see what the next char is
 146  10230 int nextChar = _extendedReader.peek();
 147   
 148    // if at end of stream or the char is one of the terminators, stop
 149  10230 if (nextChar < 0 || terminators.matches((char) nextChar))
 150  498 break;
 151   
 152  9732 try {
 153    // read the char (and possibly unquote it)
 154  9732 char ch = readQuotedChar();
 155  9727 buf.append(ch);
 156    } catch (IgnoreCharacterException e) {
 157    // simply ignore -- no character was read
 158    }
 159    }
 160   
 161  498 return buf.toString();
 162    }
 163   
 164   
 165  9732 private char readQuotedChar() throws IOException, IgnoreCharacterException
 166    {
 167  9732 int nextChar = _extendedReader.read();
 168  9732 if (nextChar < 0)
 169  0 throw new IgnoreCharacterException();
 170  9732 char ch = (char) nextChar;
 171   
 172    // if the char is not the quotation char, simply return it
 173  9732 if (ch != '\\')
 174  9720 return ch;
 175   
 176    // the character is a quotation character. unquote it
 177  12 nextChar = _extendedReader.read();
 178   
 179    // if at the end of the stream, stop
 180  12 if (nextChar < 0)
 181  0 throw new IgnoreCharacterException();
 182   
 183  12 ch = (char) nextChar;
 184  12 switch (ch) {
 185  3 case 'u' :
 186  3 char res = 0;
 187  3 for (int i = 0; i < 4; i++) {
 188  7 nextChar = _extendedReader.read();
 189  7 if (nextChar < 0)
 190  1 throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
 191  6 char digitChar = (char) nextChar;
 192  6 int digit = HEX_DIGITS.indexOf(Character.toUpperCase(digitChar));
 193  6 if (digit < 0)
 194  1 throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
 195  5 res = (char) (res * 16 + digit);
 196    }
 197  1 return res;
 198   
 199  3 case '\r' :
 200    // if the next char is \n, read it and fall through
 201  3 nextChar = _extendedReader.peek();
 202  3 if (nextChar == '\n')
 203  3 _extendedReader.read();
 204  0 case '\n' :
 205  3 _extendedReader.skipCharacters(WHITESPACE);
 206  3 throw new IgnoreCharacterException();
 207   
 208  0 case 't' : return '\t';
 209  0 case 'n' : return '\n';
 210  0 case 'r' : return '\r';
 211  6 default: return ch;
 212    }
 213    }
 214   
 215   
 216    private static class IgnoreCharacterException extends Exception
 217    {
 218    private static final long serialVersionUID = 8366308710256427596L;
 219    }
 220    }