Coverage Report - org.apache.commons.configuration.plist.PropertyListConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyListConfiguration
80%
66/83
95%
19/20
3,556
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.configuration.plist;
 19  
 
 20  
 import java.io.File;
 21  
 import java.io.PrintWriter;
 22  
 import java.io.Reader;
 23  
 import java.io.Writer;
 24  
 import java.net.URL;
 25  
 import java.util.ArrayList;
 26  
 import java.util.Iterator;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 
 30  
 import org.apache.commons.codec.binary.Hex;
 31  
 import org.apache.commons.configuration.AbstractHierarchicalFileConfiguration;
 32  
 import org.apache.commons.configuration.Configuration;
 33  
 import org.apache.commons.configuration.ConfigurationException;
 34  
 import org.apache.commons.configuration.HierarchicalConfiguration;
 35  
 import org.apache.commons.configuration.MapConfiguration;
 36  
 import org.apache.commons.lang.StringUtils;
 37  
 
 38  
 /**
 39  
  * NeXT / OpenStep style configuration.
 40  
  * (http://developer.apple.com/documentation/Cocoa/Conceptual/PropertyLists/Concepts/OldStylePListsConcept.html)
 41  
  *
 42  
  * <p>Example:</p>
 43  
  * <pre>
 44  
  * {
 45  
  *     foo = "bar";
 46  
  *
 47  
  *     array = ( value1, value2, value3 );
 48  
  *
 49  
  *     data = &lt;4f3e0145ab>;
 50  
  *
 51  
  *     nested =
 52  
  *     {
 53  
  *         key1 = value1;
 54  
  *         key2 = value;
 55  
  *         nested =
 56  
  *         {
 57  
  *             foo = bar
 58  
  *         }
 59  
  *     }
 60  
  * }
 61  
  * </pre>
 62  
  *
 63  
  * @since 1.2
 64  
  *
 65  
  * @author Emmanuel Bourg
 66  
  * @version $Revision: 439648 $, $Date: 2006-09-02 22:42:10 +0200 (Sa, 02 Sep 2006) $
 67  
  */
 68  
 public class PropertyListConfiguration extends AbstractHierarchicalFileConfiguration
 69  
 {
 70  
     /**
 71  
      * The serial version UID.
 72  
      */
 73  
     private static final long serialVersionUID = 3227248503779092127L;
 74  
 
 75  
     /** Size of the indentation for the generated file. */
 76  
     private static final int INDENT_SIZE = 4;
 77  
 
 78  
     /**
 79  
      * Creates an empty PropertyListConfiguration object which can be
 80  
      * used to synthesize a new plist file by adding values and
 81  
      * then saving().
 82  
      */
 83  
     public PropertyListConfiguration()
 84  126
     {
 85  126
     }
 86  
 
 87  
     /**
 88  
      * Creates and loads the property list from the specified file.
 89  
      *
 90  
      * @param fileName The name of the plist file to load.
 91  
      * @throws ConfigurationException Error while loading the plist file
 92  
      */
 93  
     public PropertyListConfiguration(String fileName) throws ConfigurationException
 94  
     {
 95  0
         super(fileName);
 96  0
     }
 97  
 
 98  
     /**
 99  
      * Creates and loads the property list from the specified file.
 100  
      *
 101  
      * @param file The plist file to load.
 102  
      * @throws ConfigurationException Error while loading the plist file
 103  
      */
 104  
     public PropertyListConfiguration(File file) throws ConfigurationException
 105  
     {
 106  1
         super(file);
 107  1
     }
 108  
 
 109  
     /**
 110  
      * Creates and loads the property list from the specified URL.
 111  
      *
 112  
      * @param url The location of the plist file to load.
 113  
      * @throws ConfigurationException Error while loading the plist file
 114  
      */
 115  
     public PropertyListConfiguration(URL url) throws ConfigurationException
 116  
     {
 117  0
         super(url);
 118  0
     }
 119  
 
 120  
     public void load(Reader in) throws ConfigurationException
 121  
     {
 122  15
         PropertyListParser parser = new PropertyListParser(in);
 123  
         try
 124  
         {
 125  
 
 126  15
             HierarchicalConfiguration config = parser.parse();
 127  14
             setRoot(config.getRoot());
 128  14
         }
 129  
         catch (ParseException e)
 130  
         {
 131  1
             throw new ConfigurationException(e);
 132  
         }
 133  14
     }
 134  
 
 135  
     public void save(Writer out) throws ConfigurationException
 136  
     {
 137  1
         PrintWriter writer = new PrintWriter(out);
 138  1
         printNode(writer, 0, getRoot());
 139  1
         writer.flush();
 140  1
     }
 141  
 
 142  
     /**
 143  
      * Append a node to the writer, indented according to a specific level.
 144  
      */
 145  
     private void printNode(PrintWriter out, int indentLevel, Node node)
 146  
     {
 147  22
         String padding = StringUtils.repeat(" ", indentLevel * INDENT_SIZE);
 148  
 
 149  22
         if (node.getName() != null)
 150  
         {
 151  19
             out.print(padding + quoteString(node.getName()) + " = ");
 152  
         }
 153  
 
 154  
         // get all non trivial nodes
 155  22
         List children = new ArrayList(node.getChildren());
 156  22
         Iterator it = children.iterator();
 157  64
         while (it.hasNext())
 158  
         {
 159  20
             Node child = (Node) it.next();
 160  20
             if (child.getValue() == null && (child.getChildren() == null || child.getChildren().isEmpty()))
 161  
             {
 162  1
                 it.remove();
 163  
             }
 164  
         }
 165  
 
 166  22
         if (!children.isEmpty())
 167  
         {
 168  
             // skip a line, except for the root dictionary
 169  7
             if (indentLevel > 0)
 170  
             {
 171  6
                 out.println();
 172  
             }
 173  
 
 174  7
             out.println(padding + "{");
 175  
 
 176  
             // display the children
 177  7
             it = children.iterator();
 178  33
             while (it.hasNext())
 179  
             {
 180  19
                 Node child = (Node) it.next();
 181  
 
 182  19
                 printNode(out, indentLevel + 1, child);
 183  
 
 184  
                 // add a semi colon for elements that are not dictionaries
 185  19
                 Object value = child.getValue();
 186  19
                 if (value != null && !(value instanceof Map) && !(value instanceof Configuration))
 187  
                 {
 188  15
                     out.println(";");
 189  
                 }
 190  
 
 191  
                 // skip a line after arrays and dictionaries
 192  19
                 if (it.hasNext() && (value == null || value instanceof List))
 193  
                 {
 194  6
                     out.println();
 195  
                 }
 196  
             }
 197  
 
 198  7
             out.print(padding + "}");
 199  
 
 200  
             // line feed if the dictionary is not in an array
 201  7
             if (node.getParent() != null)
 202  
             {
 203  4
                 out.println();
 204  
             }
 205  
         }
 206  
         else
 207  
         {
 208  
             // display the leaf value
 209  15
             Object value = node.getValue();
 210  15
             printValue(out, indentLevel, value);
 211  
         }
 212  22
     }
 213  
 
 214  
     /**
 215  
      * Append a value to the writer, indented according to a specific level.
 216  
      */
 217  
     private void printValue(PrintWriter out, int indentLevel, Object value)
 218  
     {
 219  26
         String padding = StringUtils.repeat(" ", indentLevel * INDENT_SIZE);
 220  
 
 221  26
         if (value instanceof List)
 222  
         {
 223  6
             out.print("( ");
 224  6
             Iterator it = ((List) value).iterator();
 225  23
             while (it.hasNext())
 226  
             {
 227  11
                 printValue(out, indentLevel + 1, it.next());
 228  11
                 if (it.hasNext())
 229  
                 {
 230  6
                     out.print(", ");
 231  
                 }
 232  
             }
 233  6
             out.print(" )");
 234  
         }
 235  20
         else if (value instanceof HierarchicalConfiguration)
 236  
         {
 237  2
             printNode(out, indentLevel, ((HierarchicalConfiguration) value).getRoot());
 238  
         }
 239  18
         else if (value instanceof Configuration)
 240  
         {
 241  
             // display a flat Configuration as a dictionary
 242  0
             out.println();
 243  0
             out.println(padding + "{");
 244  
 
 245  0
             Configuration config = (Configuration) value;
 246  0
             Iterator it = config.getKeys();
 247  0
             while (it.hasNext())
 248  
             {
 249  0
                 String key = (String) it.next();
 250  0
                 Node node = new Node(key);
 251  0
                 node.setValue(config.getProperty(key));
 252  
 
 253  0
                 printNode(out, indentLevel + 1, node);
 254  0
                 out.println(";");
 255  
             }
 256  0
             out.println(padding + "}");
 257  
         }
 258  18
         else if (value instanceof Map)
 259  
         {
 260  
             // display a Map as a dictionary
 261  0
             Map map = (Map) value;
 262  0
             printValue(out, indentLevel, new MapConfiguration(map));
 263  
         }
 264  18
         else if (value instanceof byte[])
 265  
         {
 266  2
             out.print("<" + new String(Hex.encodeHex((byte[]) value)) + ">");
 267  
         }
 268  16
         else if (value != null)
 269  
         {
 270  16
             out.print(quoteString(String.valueOf(value)));
 271  
         }
 272  26
     }
 273  
 
 274  
     /**
 275  
      * Quote the specified string if necessary, that's if the string contains:
 276  
      * <ul>
 277  
      *   <li>a space character (' ', '\t', '\r', '\n')</li>
 278  
      *   <li>a quote '"'</li>
 279  
      *   <li>special characters in plist files ('(', ')', '{', '}', '=', ';', ',')</li>
 280  
      * </ul>
 281  
      * Quotes within the string are escaped.
 282  
      *
 283  
      * <p>Examples:</p>
 284  
      * <ul>
 285  
      *   <li>abcd -> abcd</li>
 286  
      *   <li>ab cd -> "ab cd"</li>
 287  
      *   <li>foo"bar -> "foo\"bar"</li>
 288  
      *   <li>foo;bar -> "foo;bar"</li>
 289  
      * </ul>
 290  
      */
 291  
     String quoteString(String s)
 292  
     {
 293  40
         if (s == null)
 294  
         {
 295  1
             return null;
 296  
         }
 297  
 
 298  39
         if (s.indexOf(' ') != -1
 299  
                 || s.indexOf('\t') != -1
 300  
                 || s.indexOf('\r') != -1
 301  
                 || s.indexOf('\n') != -1
 302  
                 || s.indexOf('"') != -1
 303  
                 || s.indexOf('(') != -1
 304  
                 || s.indexOf(')') != -1
 305  
                 || s.indexOf('{') != -1
 306  
                 || s.indexOf('}') != -1
 307  
                 || s.indexOf('=') != -1
 308  
                 || s.indexOf(',') != -1
 309  
                 || s.indexOf(';') != -1)
 310  
         {
 311  5
             s = StringUtils.replace(s, "\"", "\\\"");
 312  5
             s = "\"" + s + "\"";
 313  
         }
 314  
 
 315  39
         return s;
 316  
     }
 317  
 }