##### 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.5555555555555554;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 |  |
     }