001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.lib.ddl.util;
043:
044: import java.io.*;
045: import java.text.ParseException;
046: import java.util.HashMap;
047: import java.util.Map;
048: import java.util.Set;
049: import java.util.Vector;
050:
051: import java.text.MessageFormat;
052: import org.openide.util.NbBundle;
053:
054: /** Reader for "plist" format. This format uses {} brackets to enclose dictionary
055: * data (Map) and () braces for array data (Collection). Returns Map with data.
056: *
057: * @author Slavek Psenicka
058: */
059: public class PListReader {
060:
061: protected StreamTokenizer tokenizer = null;
062:
063: public static Map read(String file) throws FileNotFoundException,
064: ParseException, IOException {
065: PListReader reader = new PListReader(file);
066: return reader.getData();
067: }
068:
069: /** Constructor
070: * Initializes reader with contents of file
071: * @param file File to read
072: */
073: public PListReader(String file) throws FileNotFoundException,
074: ParseException, IOException {
075: BufferedReader buffreader = new BufferedReader(new FileReader(
076: file));
077: tokenizer = createTokenizer(buffreader);
078: }
079:
080: /** Constructor
081: * Initializes reader with contents of file
082: * @param file File to read
083: */
084: public PListReader(File file) throws FileNotFoundException,
085: ParseException, IOException {
086: BufferedReader buffreader = new BufferedReader(new FileReader(
087: file));
088: tokenizer = createTokenizer(buffreader);
089: }
090:
091: /** Constructor
092: * Initializes reader with stream
093: * @param stream Stream to read
094: */
095: public PListReader(InputStream stream)
096: throws FileNotFoundException, ParseException, IOException {
097: BufferedReader buffreader = new BufferedReader(
098: new InputStreamReader(stream));
099: tokenizer = createTokenizer(buffreader);
100: }
101:
102: /** Prepares tokenizer for this format.
103: * Enables both comment styles.
104: * @param buffreader Reader
105: * @return Newly created tokenizer
106: */
107: private StreamTokenizer createTokenizer(BufferedReader buffreader) {
108: StreamTokenizer tok = new StreamTokenizer(buffreader);
109: tok.slashStarComments(true);
110: tok.slashSlashComments(true);
111: tok.wordChars(95, 95);
112: tok.wordChars(126, 126);
113: tok.wordChars(33, 33);
114: tok.wordChars(96, 96);
115: tok.wordChars(39, 39);
116: tok.wordChars(35, 35);
117: tok.wordChars(36, 36);
118: tok.wordChars(37, 37);
119: tok.wordChars(94, 94);
120: tok.wordChars(38, 38);
121: tok.wordChars(42, 42);
122: tok.wordChars(45, 45);
123: tok.wordChars(43, 43);
124: tok.wordChars(124, 124);
125: tok.wordChars(63, 63);
126: tok.wordChars(46, 46);
127: tok.quoteChar(34);
128: return tok;
129: }
130:
131: /** Reads data from tokenizer.
132: * Parses given data tokenizer and produces data.
133: * @param tokenizer Used tokenizer
134: * @return Parsed data structure.
135: */
136: private Object read(StreamTokenizer tokenizer)
137: throws FileNotFoundException, ParseException, IOException {
138: DictionaryNode node = new DictionaryNode(tokenizer);
139: return node.getBindings();
140: }
141:
142: /** Reads data from tokenizer.
143: * Parses given data tokenizer and produces data.
144: * @param tokenizer Used tokenizer
145: * @return Parsed data structure.
146: */
147: public HashMap getData() throws FileNotFoundException,
148: ParseException, IOException {
149: return (HashMap) read(tokenizer);
150: }
151:
152: /** Returns prepared tokenizer.
153: * @return Prepared tokenizer.
154: */
155: public StreamTokenizer getTokenizer() {
156: return tokenizer;
157: }
158:
159: /** Inner superclass for nodes */
160: abstract class Node {
161: /** Parses expected character from stream.
162: * Throws ParseException if expected character was not found.
163: * @param tokenizer Used tokenizer
164: * @param charcode Expected character code
165: */
166: public void parseChar(StreamTokenizer tokenizer, int charcode)
167: throws IOException, ParseException {
168: tokenizer.nextToken();
169: if (tokenizer.ttype != charcode) {
170: char[] charr = new char[1];
171: charr[0] = (char) charcode;
172: //throw new ParseException("expected '"+new String(charr)+"', found: "+tokenizer.toString(), tokenizer.lineno());
173: throw new ParseException(
174: MessageFormat
175: .format(
176: NbBundle
177: .getBundle(
178: "org.netbeans.lib.ddl.resources.Bundle")
179: .getString(
180: "EXC_Expected"), // NOI18N
181: new String[] {
182: new String(charr),
183: tokenizer.toString() }),
184: tokenizer.lineno());
185: }
186: }
187:
188: /** Parses expected character from stream.
189: * Throws ParseException if expected character was not found.
190: * @param tokenizer Used tokenizer
191: * @param charcode Expected character code
192: */
193: public Object parseNumber(StreamTokenizer tokenizer)
194: throws IOException {
195: String s5 = Double.toString(tokenizer.nval);
196: tokenizer.nextToken();
197: if (tokenizer.ttype == -2) {
198: while (tokenizer.ttype == -2) {
199: s5 = s5 + Double.toString(tokenizer.nval);
200: tokenizer.nextToken();
201: }
202:
203: tokenizer.pushBack();
204: return s5;
205: }
206:
207: tokenizer.pushBack();
208: double d = Math.rint(tokenizer.nval);
209: if (d == tokenizer.nval)
210: return new Integer((int) d);
211: return new Double(tokenizer.nval);
212: }
213: }
214:
215: /**
216: * Inner class for dictionary node.
217: * @author Slavek Psenicka
218: */
219: class DictionaryNode extends Node {
220: /** Read values */
221: HashMap bindings;
222:
223: /* Constructor */
224: public DictionaryNode(StreamTokenizer tokenizer)
225: throws ParseException, IOException {
226: bindings = new HashMap();
227: parse(tokenizer);
228: }
229:
230: /* Method for reading data from tokenizer
231: * Prepares structure into bindings map.
232: * @param tokenizer Used tokenizer
233: */
234: public void parse(StreamTokenizer tokenizer)
235: throws ParseException, IOException {
236: String key = null;
237: Object object = null;
238:
239: try {
240:
241: // left bracket
242: parseChar(tokenizer, 123);
243:
244: while (true) {
245:
246: // key or right bracket
247: tokenizer.nextToken();
248: switch (tokenizer.ttype) {
249: case StreamTokenizer.TT_WORD:
250: key = tokenizer.sval;
251: break;
252: case 34:
253: key = tokenizer.sval;
254: break;
255: case 125:
256: throw new EOFException();
257: default:
258: //throw new ParseException("unexpected key, found: "+tokenizer.toString(), tokenizer.lineno());
259: throw new ParseException(
260: MessageFormat
261: .format(
262: NbBundle
263: .getBundle(
264: "org.netbeans.lib.ddl.resources.Bundle")
265: .getString(
266: "EXC_UnexpectedKey"), // NOI18N
267: new String[] { tokenizer
268: .toString() }),
269: tokenizer.lineno());
270: }
271:
272: // =
273: parseChar(tokenizer, 61);
274:
275: // object
276: tokenizer.nextToken();
277: switch (tokenizer.ttype) {
278: case StreamTokenizer.TT_WORD:
279: object = tokenizer.sval;
280: break;
281: case 34:
282: object = tokenizer.sval;
283: break;
284: case 123:
285: tokenizer.pushBack();
286: object = (Object) new DictionaryNode(tokenizer)
287: .getBindings();
288: break;
289: case 40:
290: tokenizer.pushBack();
291: object = (Object) new ArrayNode(tokenizer)
292: .getBindings();
293: break;
294: case StreamTokenizer.TT_NUMBER:
295: object = parseNumber(tokenizer);
296: break;
297: default:
298: throw new ParseException(
299: MessageFormat
300: .format(
301: NbBundle
302: .getBundle(
303: "org.netbeans.lib.ddl.resources.Bundle")
304: .getString(
305: "EXC_ExpectedObject"), // NOI18N
306: new String[] { tokenizer
307: .toString() }),
308: tokenizer.lineno());
309: }
310:
311: // ;
312: parseChar(tokenizer, 59);
313: bindings.put(key, object);
314: }
315:
316: } catch (EOFException e) {
317: }
318: }
319:
320: /** Returns value for specified key */
321: public Object get(String s) {
322: return bindings.get(s);
323: }
324:
325: /** Returns all keys */
326: public Set getKeys() {
327: return bindings.keySet();
328: }
329:
330: /** Returns whole binding map */
331: public HashMap getBindings() {
332: return bindings;
333: }
334: }
335:
336: /**
337: * Inner class for array node.
338: * @author Slavek Psenicka
339: */
340: class ArrayNode extends Node {
341: /* Data */
342: Vector bindings;
343:
344: /** Constructor */
345: public ArrayNode(StreamTokenizer tokenizer)
346: throws ParseException, IOException {
347: bindings = new Vector();
348: parse(tokenizer);
349: }
350:
351: /* Method for reading data from tokenizer
352: * Prepares structure into bindings map.
353: * @param tokenizer Used tokenizer
354: */
355: public void parse(StreamTokenizer tokenizer)
356: throws ParseException, IOException {
357: Object object = null;
358:
359: try {
360:
361: // left bracket
362: parseChar(tokenizer, 40);
363:
364: while (true) {
365:
366: // object
367: tokenizer.nextToken();
368: switch (tokenizer.ttype) {
369: case StreamTokenizer.TT_WORD:
370: object = tokenizer.sval;
371: break;
372: case 34:
373: object = tokenizer.sval;
374: break;
375: case 41:
376: throw new EOFException();
377: case 123:
378: tokenizer.pushBack();
379: object = (Object) new DictionaryNode(tokenizer)
380: .getBindings();
381: break;
382: case 40:
383: tokenizer.pushBack();
384: object = new ArrayNode(tokenizer).getBindings();
385: break;
386: case StreamTokenizer.TT_NUMBER:
387: object = parseNumber(tokenizer);
388: break;
389: default:
390: throw new ParseException(
391: MessageFormat
392: .format(
393: NbBundle
394: .getBundle(
395: "org.netbeans.lib.ddl.resources.Bundle")
396: .getString(
397: "EXC_ExpectedObject"), // NOI18N
398: new String[] { tokenizer
399: .toString() }),
400: tokenizer.lineno());
401: }
402:
403: bindings.add(object);
404:
405: // ,
406: tokenizer.nextToken();
407: switch (tokenizer.ttype) {
408: case 41:
409: throw new EOFException();
410: case 44:
411: break;
412: default:
413: throw new ParseException(
414: MessageFormat
415: .format(
416: NbBundle
417: .getBundle(
418: "org.netbeans.lib.ddl.resources.Bundle")
419: .getString(
420: "EXC_Expected"), // NOI18N
421: new String[] {
422: "','",
423: tokenizer
424: .toString() }), // NOI18N
425: tokenizer.lineno());
426: }
427: }
428:
429: } catch (EOFException e) {
430: }
431: }
432:
433: /** Returns while binding array */
434: public Vector getBindings() {
435: return bindings;
436: }
437: }
438: }
|