Source code: org/objectstyle/cayenne/wocompat/PropertyListSerialization.java
1 /* ====================================================================
2 *
3 * The ObjectStyle Group Software License, Version 1.0
4 *
5 * Copyright (c) 2002-2003 The ObjectStyle Group
6 * and individual authors of the software. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution, if
21 * any, must include the following acknowlegement:
22 * "This product includes software developed by the
23 * ObjectStyle Group (http://objectstyle.org/)."
24 * Alternately, this acknowlegement may appear in the software itself,
25 * if and wherever such third-party acknowlegements normally appear.
26 *
27 * 4. The names "ObjectStyle Group" and "Cayenne"
28 * must not be used to endorse or promote products derived
29 * from this software without prior written permission. For written
30 * permission, please contact andrus@objectstyle.org.
31 *
32 * 5. Products derived from this software may not be called "ObjectStyle"
33 * nor may "ObjectStyle" appear in their names without prior written
34 * permission of the ObjectStyle Group.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 * ====================================================================
49 *
50 * This software consists of voluntary contributions made by many
51 * individuals on behalf of the ObjectStyle Group. For more
52 * information on the ObjectStyle Group, please see
53 * <http://objectstyle.org/>.
54 *
55 */
56 package org.objectstyle.cayenne.wocompat;
57
58 import java.io.BufferedWriter;
59 import java.io.File;
60 import java.io.FileNotFoundException;
61 import java.io.FileWriter;
62 import java.io.IOException;
63 import java.io.InputStream;
64 import java.io.Writer;
65 import java.util.Iterator;
66 import java.util.List;
67 import java.util.Map;
68
69 import org.objectstyle.cayenne.CayenneRuntimeException;
70 import org.objectstyle.cayenne.wocompat.parser.Parser;
71
72 /**
73 * A <b>PropertyListSerialization</b> is a utility class
74 * that reads and stores files in NeXT/Apple
75 * property list format. Unlike corresponding WebObjects
76 * class, <code>PropertyListSerialization</code> uses standard
77 * Java collections (lists and maps) to store property lists.
78 *
79 * @author Andrei Adamchik
80 */
81 public class PropertyListSerialization {
82 /**
83 * Reads a property list file. Returns a property list object, that is
84 * normally a java.util.List or a java.util.Map, but can also be a String
85 * or a Number.
86 */
87 public static Object propertyListFromFile(File f) throws FileNotFoundException {
88 if (!f.isFile()) {
89 throw new FileNotFoundException("No such file: " + f);
90 }
91
92 return new Parser(f).propertyList();
93 }
94
95 /**
96 * Reads a property list data from InputStream. Returns a property list o
97 * bject, that is normally a java.util.List or a java.util.Map,
98 * but can also be a String or a Number.
99 */
100 public static Object propertyListFromStream(InputStream in) {
101 return new Parser(in).propertyList();
102 }
103
104 /**
105 * Saves property list to file.
106 */
107 public static void propertyListToFile(File f, Object plist) {
108 try {
109 BufferedWriter out = new BufferedWriter(new FileWriter(f));
110 try {
111 writeObject("", out, plist);
112 } finally {
113 out.close();
114 }
115 } catch (IOException ioex) {
116 throw new CayenneRuntimeException("Error saving plist.", ioex);
117 }
118 }
119
120 /**
121 * Internal method to recursively write a property list object.
122 */
123 protected static void writeObject(String offset, Writer out, Object plist)
124 throws IOException {
125 if (plist == null) {
126 return;
127 }
128
129 if (plist instanceof List) {
130 List list = (List) plist;
131
132 out.write('\n');
133 out.write(offset);
134
135 if (list.size() == 0) {
136 out.write("()");
137 return;
138 }
139
140 out.write("(\n");
141
142 String childOffset = offset + " ";
143 Iterator it = list.iterator();
144 boolean appended = false;
145 while (it.hasNext()) {
146 // Java collections can contain nulls, skip them
147 Object obj = it.next();
148 if (obj != null) {
149 if (appended) {
150 out.write(", \n");
151 }
152
153 out.write(childOffset);
154 writeObject(childOffset, out, obj);
155 appended = true;
156 }
157 }
158
159 out.write('\n');
160 out.write(offset);
161 out.write(')');
162 } else if (plist instanceof Map) {
163 Map map = (Map) plist;
164 out.write('\n');
165 out.write(offset);
166
167 if (map.size() == 0) {
168 out.write("{}");
169 return;
170 }
171
172 out.write("{");
173
174 String childOffset = offset + " ";
175
176 Iterator it = map.keySet().iterator();
177 while (it.hasNext()) {
178 // Java collections can contain nulls, skip them
179 Object key = it.next();
180 if (key == null) {
181 continue;
182 }
183 Object obj = map.get(key);
184 if (obj == null) {
185 continue;
186 }
187 out.write('\n');
188 out.write(childOffset);
189 out.write(quoteString(key.toString()));
190 out.write(" = ");
191 writeObject(childOffset, out, obj);
192 out.write(';');
193 }
194
195 out.write('\n');
196 out.write(offset);
197 out.write('}');
198 } else if (plist instanceof String) {
199 out.write(quoteString(plist.toString()));
200 } else if (plist instanceof Number) {
201 out.write(plist.toString());
202 } else {
203 throw new CayenneRuntimeException(
204 "Unsupported class for property list serialization: "
205 + plist.getClass().getName());
206 }
207 }
208
209 /**
210 * Escapes all doublequotes and backslashes.
211 */
212 protected static String escapeString(String str) {
213 char[] chars = str.toCharArray();
214 int len = chars.length;
215 StringBuffer buf = new StringBuffer(len + 3);
216
217 for (int i = 0; i < len; i++) {
218 if (chars[i] == '\"' || chars[i] == '\\') {
219 buf.append('\\');
220 }
221 buf.append(chars[i]);
222 }
223
224 return buf.toString();
225 }
226
227 /**
228 * Returns a quoted Stirng, with all the escapes preprocessed.
229 * May return an unquoted String if it contains on special
230 * characters, such as spaces, doublequotes and braces.
231 */
232 protected static String quoteString(String str) {
233 boolean shouldQuote = false;
234
235 // scan string for special chars,
236 // if we have them, string must be quoted
237 String special = " \\\"{}();,-\'";
238 char[] chars = str.toCharArray();
239 int len = chars.length;
240 for (int i = 0; i < len; i++) {
241 if (special.indexOf(chars[i]) >= 0) {
242 shouldQuote = true;
243 break;
244 }
245 }
246
247 str = escapeString(str);
248 return (shouldQuote) ? '\"' + str + '\"' : str;
249 }
250 }