001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.installer;
028:
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.io.InputStreamReader;
032: import java.io.Reader;
033:
034: import java.util.Vector;
035:
036: import com.sun.midp.util.Properties;
037:
038: /*
039: * EBNF for parsing Application Descriptors, from MIDP 1.0 Spec.
040: * except that the WSP before and after attrvalue in attrline is optional.
041: *
042: * appldesc: *attrline
043: * attrline: attrname ":" *WSP attrvalue *WSP newline
044: *
045: * attrname: 1*<any Unicode char except CTLs or separators>
046: * attrvalue: *valuechar | valuechar *(valuechar | WSP) valuechar
047: * valuechar: <any Unicode char except CTLs and WSP>
048: *
049: * newline: CR LF | LF
050: * CR: <Unicode carriage return (0x000D)>
051: * LF: <Unicode linefeed (0x000A)>
052: *
053: * WSP: 1*(SP | HT)
054: * SP: <Unicode space (0x0020)>
055: * HT: <Unicode horizontal-tab (0x0009)>
056: * CTL: <Unicode characters 0x0000 - 0x001F and 0x007F>
057: * separator: "(" | ")" | "<" | ">" | "@"
058: * | "," | ";" | ":" | "'" | """
059: * | "/" | "[" | "]" | "?" | "="
060: * | "{" | "}" | SP | HT
061: */
062:
063: /**
064: * This class represents a set of properties loaded from a MIDP Java
065: * Application Descriptor. The parsing of descriptor is more relaxed than
066: * the MIDP 1.0 specification. First, the white space that is required
067: * before and after the property value is optional. Second, any extra
068: * carriage returns and end of file characters are ignored. Third, the key
069: * of the property is only checked for whitespace. Fourth, blanklines
070: * are allowed so the save code when parsing JAR manifests.
071: * <p>
072: * The set of properties, though not strictly ordered, will be stored
073: * in the same order it was read in or created, with modifications being
074: * appended to the end of the list by default.
075: * <p>
076: * If an alternate encoding type is not given, when saving properties
077: * to a stream or loading them from a stream, the ISO 8859-1 character
078: * encoding is used.
079: */
080:
081: public class JadProperties extends Properties {
082: /** Horizontal Tab - Unicode character 0x09. */
083: protected static final int HT = 0x09;
084:
085: /** Line Feed - Unicode character 0x0A. */
086: protected static final int LF = 0x0A;
087:
088: /** Carriage Return - Unicode character 0x0D. */
089: protected static final int CR = 0x0D;
090:
091: /** End Of File - Unicode character 0x1A. */
092: protected static final int EOF = 0x1A;
093:
094: /** SPace - Unicode character 0x20. */
095: protected static final int SP = 0x20;
096:
097: /** Buffers one line from the stream. */
098: protected char[] lineBuffer = null;
099:
100: /**
101: * Constructor - creates an empty property list.
102: */
103: public JadProperties() {
104: }
105:
106: /**
107: * Reads a JAD (key and element pairs) from the input stream.
108: * The stream uses the character encoding specified by <code>enc</code>
109: * <p>
110: * Every property occupies one line of the input stream. Each line
111: * is terminated by a line terminator which can be a LF, or (CR LF).
112: * Lines from the input stream are processed until
113: * end of file is reached on the input stream.
114: * <p>
115: * Every line describes one property to be added to the table.
116: * The key consists of all the characters from beginning of the line
117: * up to, but not including the first ASCII <code>:</code>.
118: * All remaining characters on the line become part of the associated
119: * element. The element is also trimmed of leading and trailing
120: * whitespace.
121: * <p>
122: * As an example, each of the following line specifies the key
123: * <code>"Truth"</code> and the associated element value
124: * <code>"Beauty"</code>:
125: * <p>
126: * <pre>
127: * Truth: Beauty
128: * </pre>
129: * <p>
130: * This method will try to continue after a format error and load as
131: * many properties it can, but throw the last error encountered.
132: *
133: * @param inStream the input stream.
134: * @param enc character encoding used on input stream,
135: * can be null to get the default (UTF-8)
136: * @exception IOException if an error occurred when reading from the
137: * input stream.
138: * @exception InvalidJadException if the JAD is not formatted correctly.
139: */
140: public synchronized void load(InputStream inStream, String enc)
141: throws IOException, InvalidJadException {
142: partialLoad(inStream, enc, Integer.MAX_VALUE);
143: }
144:
145: /**
146: * Loads up a given number of properties from a JAD.
147: * Used when authenticating a JAD.
148: *
149: * @param inStream the input stream.
150: * @param enc character encoding used on input stream,
151: * null for the default encoding (UTF-8)
152: * @param propertiesToLoad maximum number of properties to load
153: * @exception IOException if an error occurred when reading from the
154: * input stream.
155: * @exception InvalidJadException if the JAD is not formatted correctly.
156: */
157: public void partialLoad(InputStream inStream, String enc,
158: int propertiesToLoad) throws IOException,
159: InvalidJadException {
160: Reader in;
161: String line;
162: int endOfKey;
163: String key = null;
164: int startOfValue;
165: String value = null;
166: InvalidJadException jadException = null;
167:
168: if (enc == null) {
169: in = new InputStreamReader(inStream, "UTF-8");
170: } else {
171: in = new InputStreamReader(inStream, enc);
172: }
173:
174: lineBuffer = new char[512];
175:
176: for (int i = 0; i < propertiesToLoad; i++) {
177: // Get next line
178: line = readLine(in);
179: if (line == null) {
180: break;
181: }
182:
183: // blank line separate groups of properties
184: if (line.length() == 0) {
185: continue;
186: }
187:
188: endOfKey = line.indexOf(":");
189: if (endOfKey == -1) {
190: jadException = new InvalidJadException(
191: InvalidJadException.INVALID_KEY, line);
192: continue;
193: }
194:
195: key = line.substring(0, endOfKey);
196:
197: if (key == null || key.length() == 0) {
198: jadException = new InvalidJadException(
199: InvalidJadException.INVALID_KEY, line);
200: continue;
201: }
202:
203: if (!checkKeyChars(key)) {
204: jadException = new InvalidJadException(
205: InvalidJadException.INVALID_KEY, line);
206: continue;
207: }
208:
209: startOfValue = endOfKey + 1;
210: value = line.substring(startOfValue, line.length());
211: value = value.trim();
212: if (value == null) {
213: jadException = new InvalidJadException(
214: InvalidJadException.INVALID_VALUE, key);
215: continue;
216: }
217:
218: if (!checkValueChars(value)) {
219: jadException = new InvalidJadException(
220: InvalidJadException.INVALID_VALUE, key);
221: continue;
222: }
223:
224: putProperty(key, value);
225: }
226:
227: // we only need the line buffer while loading, so let it be reclaimed
228: lineBuffer = null;
229:
230: if (jadException != null) {
231: throw jadException;
232: }
233: }
234:
235: /**
236: * Loads properties from the input stream using the default
237: * character encoding. Currently the default encoding is UTF8.
238: *
239: * @see #load(InputStream inStream, String enc)
240: * @param inStream the input stream.
241: * @exception IOException if an error occurred when reading from the
242: * input stream.
243: * @exception InvalidJadException if the JAD is not formatted correctly.
244: */
245: public synchronized void load(InputStream inStream)
246: throws IOException, InvalidJadException {
247: load(inStream, null);
248: }
249:
250: /**
251: * Store key:value pair.
252: *
253: * @param key the key to be placed into this property list.
254: * @param value the value corresponding to <tt>key</tt>.
255: * @see #getProperty
256: */
257: protected void putProperty(String key, String value) {
258: setProperty(key, value);
259: }
260:
261: /**
262: * Reads one using a given reader. LF or CR LF end a line.
263: * The end of line and end of file characters are dropped.
264: * @param in reader for a JAD
265: * @return one line of the JAD or null at the end of the JAD
266: * @exception IOException thrown by the reader
267: */
268: protected String readLine(Reader in) throws IOException {
269: int room;
270: int offset = 0;
271: int c = 0;
272: char[] temp;
273:
274: room = lineBuffer.length;
275:
276: for (;;) {
277: c = in.read();
278: if (c == -1 || c == LF) {
279: // LF or CR LF ends a line
280: break;
281: }
282:
283: /*
284: * throw away carriage returns and the end of file character.
285: */
286: if (c == CR || c == EOF) {
287: continue;
288: }
289:
290: if (--room < 0) {
291: temp = new char[offset + 128];
292: room = temp.length - offset - 1;
293: System.arraycopy(lineBuffer, 0, temp, 0, offset);
294: lineBuffer = temp;
295: }
296:
297: lineBuffer[offset++] = (char) c;
298: }
299:
300: if ((c == -1) && (offset <= 0)) {
301: return null;
302: }
303:
304: return new String(lineBuffer, 0, offset);
305: }
306:
307: /**
308: * Check to see if all the chars in the key of a property are valid.
309: *
310: * @param key key to check
311: *
312: * @return false if a character is not valid for a key
313: */
314: protected boolean checkKeyChars(String key) {
315: char[] temp = key.toCharArray();
316: int len = temp.length;
317:
318: for (int i = 0; i < len; i++) {
319: char current = temp[i];
320:
321: if (current <= 0x1F || current == 0x7F || current == '('
322: || current == ')' || current == '<'
323: || current == '>' || current == '@'
324: || current == ',' || current == ';'
325: || current == '\'' || current == '"'
326: || current == '/' || current == '['
327: || current == ']' || current == '?'
328: || current == '=' || current == '{'
329: || current == '}' || current == SP || current == HT) {
330:
331: return false;
332: }
333: }
334:
335: return true;
336: }
337:
338: /**
339: * Check to see if all the chars in the value of a property are valid.
340: *
341: * @param value value to check
342: *
343: * @return false if a character is not valid for a value
344: */
345: protected boolean checkValueChars(String value) {
346: char[] temp = value.toCharArray();
347: int len = temp.length;
348:
349: // assume whitespace and newlines are trimmed
350: for (int i = 0; i < len; i++) {
351: char current = temp[i];
352:
353: // if current is a CTL character, throw exception
354: if ((current <= 0x1F || current == 0x7F) && (current != HT)) {
355: return false;
356: }
357: }
358:
359: return true;
360: }
361: }
|