Coverage Report - org.apache.commons.configuration.plist.PropertyListConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyListConfiguration
80%
68/85
95%
19/20
3,3
 
 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: 492216 $, $Date: 2007-01-03 17:51:24 +0100 (Mi, 03 Jan 2007) $
 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  135
     {
 85  135
     }
 86  
 
 87  
     /**
 88  
      * Creates a new instance of <code>PropertyListConfiguration</code> and
 89  
      * copies the content of the specified configuration into this object.
 90  
      *
 91  
      * @param c the configuration to copy
 92  
      * @since 1.4
 93  
      */
 94  
     public PropertyListConfiguration(HierarchicalConfiguration c)
 95  
     {
 96  1
         super(c);
 97  1
     }
 98  
 
 99  
     /**
 100  
      * Creates and loads the property list from the specified file.
 101  
      *
 102  
      * @param fileName The name of the plist file to load.
 103  
      * @throws ConfigurationException Error while loading the plist file
 104  
      */
 105  
     public PropertyListConfiguration(String fileName) throws ConfigurationException
 106  
     {
 107  0
         super(fileName);
 108  0
     }
 109  
 
 110  
     /**
 111  
      * Creates and loads the property list from the specified file.
 112  
      *
 113  
      * @param file The plist file to load.
 114  
      * @throws ConfigurationException Error while loading the plist file
 115  
      */
 116  
     public PropertyListConfiguration(File file) throws ConfigurationException
 117  
     {
 118  1
         super(file);
 119  1
     }
 120  
 
 121  
     /**
 122  
      * Creates and loads the property list from the specified URL.
 123  
      *
 124  
      * @param url The location of the plist file to load.
 125  
      * @throws ConfigurationException Error while loading the plist file
 126  
      */
 127  
     public PropertyListConfiguration(URL url) throws ConfigurationException
 128  
     {
 129  0
         super(url);
 130  0
     }
 131  
 
 132  
     public void load(Reader in) throws ConfigurationException
 133  
     {
 134  16
         PropertyListParser parser = new PropertyListParser(in);
 135  
         try
 136  
         {
 137  
 
 138  16
             HierarchicalConfiguration config = parser.parse();
 139  15
             setRoot(config.getRoot());
 140  15
         }
 141  
         catch (ParseException e)
 142  
         {
 143  1
             throw new ConfigurationException(e);
 144  
         }
 145  15
     }
 146  
 
 147  
     public void save(Writer out) throws ConfigurationException
 148  
     {
 149  1
         PrintWriter writer = new PrintWriter(out);
 150  1
         printNode(writer, 0, getRoot());
 151  1
         writer.flush();
 152  1
     }
 153  
 
 154  
     /**
 155  
      * Append a node to the writer, indented according to a specific level.
 156  
      */
 157  
     private void printNode(PrintWriter out, int indentLevel, Node node)
 158  
     {
 159  22
         String padding = StringUtils.repeat(" ", indentLevel * INDENT_SIZE);
 160  
 
 161  22
         if (node.getName() != null)
 162  
         {
 163  19
             out.print(padding + quoteString(node.getName()) + " = ");
 164  
         }
 165  
 
 166  
         // get all non trivial nodes
 167  22
         List children = new ArrayList(node.getChildren());
 168  22
         Iterator it = children.iterator();
 169  64
         while (it.hasNext())
 170  
         {
 171  20
             Node child = (Node) it.next();
 172  20
             if (child.getValue() == null && (child.getChildren() == null || child.getChildren().isEmpty()))
 173  
             {
 174  1
                 it.remove();
 175  
             }
 176  
         }
 177  
 
 178  22
         if (!children.isEmpty())
 179  
         {
 180  
             // skip a line, except for the root dictionary
 181  7
             if (indentLevel > 0)
 182  
             {
 183  6
                 out.println();
 184  
             }
 185  
 
 186  7
             out.println(padding + "{");
 187  
 
 188  
             // display the children
 189  7
             it = children.iterator();
 190  33
             while (it.hasNext())
 191  
             {
 192  19
                 Node child = (Node) it.next();
 193  
 
 194  19
                 printNode(out, indentLevel + 1, child);
 195  
 
 196  
                 // add a semi colon for elements that are not dictionaries
 197  19
                 Object value = child.getValue();
 198  19
                 if (value != null && !(value instanceof Map) && !(value instanceof Configuration))
 199  
                 {
 200  15
                     out.println(";");
 201  
                 }
 202  
 
 203  
                 // skip a line after arrays and dictionaries
 204  19
                 if (it.hasNext() && (value == null || value instanceof List))
 205  
                 {
 206  6
                     out.println();
 207  
                 }
 208  
             }
 209  
 
 210  7
             out.print(padding + "}");
 211  
 
 212  
             // line feed if the dictionary is not in an array
 213  7
             if (node.getParent() != null)
 214  
             {
 215  4
                 out.println();
 216  
             }
 217  
         }
 218  
         else
 219  
         {
 220  
             // display the leaf value
 221  15
             Object value = node.getValue();
 222  15
             printValue(out, indentLevel, value);
 223  
         }
 224  22
     }
 225  
 
 226  
     /**
 227  
      * Append a value to the writer, indented according to a specific level.
 228  
      */
 229  
     private void printValue(PrintWriter out, int indentLevel, Object value)
 230  
     {
 231  26
         String padding = StringUtils.repeat(" ", indentLevel * INDENT_SIZE);
 232  
 
 233  26
         if (value instanceof List)
 234  
         {
 235  6
             out.print("( ");
 236  6
             Iterator it = ((List) value).iterator();
 237  23
             while (it.hasNext())
 238  
             {
 239  11
                 printValue(out, indentLevel + 1, it.next());
 240  11
                 if (it.hasNext())
 241  
                 {
 242  6
                     out.print(", ");
 243  
                 }
 244  
             }
 245  6
             out.print(" )");
 246  
         }
 247  20
         else if (value instanceof HierarchicalConfiguration)
 248  
         {
 249  2
             printNode(out, indentLevel, ((HierarchicalConfiguration) value).getRoot());
 250  
         }
 251  18
         else if (value instanceof Configuration)
 252  
         {
 253  
             // display a flat Configuration as a dictionary
 254  0
             out.println();
 255  0
             out.println(padding + "{");
 256  
 
 257  0
             Configuration config = (Configuration) value;
 258  0
             Iterator it = config.getKeys();
 259  0
             while (it.hasNext())
 260  
             {
 261  0
                 String key = (String) it.next();
 262  0
                 Node node = new Node(key);
 263  0
                 node.setValue(config.getProperty(key));
 264  
 
 265  0
                 printNode(out, indentLevel + 1, node);
 266  0
                 out.println(";");
 267  
             }
 268  0
             out.println(padding + "}");
 269  
         }
 270  18
         else if (value instanceof Map)
 271  
         {
 272  
             // display a Map as a dictionary
 273  0
             Map map = (Map) value;
 274  0
             printValue(out, indentLevel, new MapConfiguration(map));
 275  
         }
 276  18
         else if (value instanceof byte[])
 277  
         {
 278  2
             out.print("<" + new String(Hex.encodeHex((byte[]) value)) + ">");
 279  
         }
 280  16
         else if (value != null)
 281  
         {
 282  16
             out.print(quoteString(String.valueOf(value)));
 283  
         }
 284  26
     }
 285  
 
 286  
     /**
 287  
      * Quote the specified string if necessary, that's if the string contains:
 288  
      * <ul>
 289  
      *   <li>a space character (' ', '\t', '\r', '\n')</li>
 290  
      *   <li>a quote '"'</li>
 291  
      *   <li>special characters in plist files ('(', ')', '{', '}', '=', ';', ',')</li>
 292  
      * </ul>
 293  
      * Quotes within the string are escaped.
 294  
      *
 295  
      * <p>Examples:</p>
 296  
      * <ul>
 297  
      *   <li>abcd -> abcd</li>
 298  
      *   <li>ab cd -> "ab cd"</li>
 299  
      *   <li>foo"bar -> "foo\"bar"</li>
 300  
      *   <li>foo;bar -> "foo;bar"</li>
 301  
      * </ul>
 302  
      */
 303  
     String quoteString(String s)
 304  
     {
 305  40
         if (s == null)
 306  
         {
 307  1
             return null;
 308  
         }
 309  
 
 310  39
         if (s.indexOf(' ') != -1
 311  
                 || s.indexOf('\t') != -1
 312  
                 || s.indexOf('\r') != -1
 313  
                 || s.indexOf('\n') != -1
 314  
                 || s.indexOf('"') != -1
 315  
                 || s.indexOf('(') != -1
 316  
                 || s.indexOf(')') != -1
 317  
                 || s.indexOf('{') != -1
 318  
                 || s.indexOf('}') != -1
 319  
                 || s.indexOf('=') != -1
 320  
                 || s.indexOf(',') != -1
 321  
                 || s.indexOf(';') != -1)
 322  
         {
 323  5
             s = StringUtils.replace(s, "\"", "\\\"");
 324  5
             s = "\"" + s + "\"";
 325  
         }
 326  
 
 327  39
         return s;
 328  
     }
 329  
 }