001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.util.jar;
019:
020: import java.io.ByteArrayOutputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.UTFDataFormatException;
024: import java.security.AccessController;
025: import java.util.ArrayList;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030:
031: import org.apache.harmony.archive.internal.nls.Messages;
032: import org.apache.harmony.luni.util.PriviAction;
033: import org.apache.harmony.luni.util.Util;
034:
035: class InitManifest {
036: private final byte[] inbuf = new byte[1024];
037:
038: private int inbufCount = 0, inbufPos = 0;
039:
040: private byte[] buffer = new byte[5];
041:
042: private char[] charbuf = new char[0];
043:
044: private final ByteArrayOutputStream out = new ByteArrayOutputStream(
045: 256);
046:
047: private String encoding;
048:
049: private boolean usingUTF8 = true;
050:
051: private final Map<String, Attributes.Name> attributeNames = new HashMap<String, Attributes.Name>();
052:
053: private final byte[] mainAttributesChunk;
054:
055: InitManifest(InputStream is, Attributes main,
056: Map<String, Attributes> entries,
057: Map<String, byte[]> chunks, String verString)
058: throws IOException {
059: encoding = AccessController
060: .doPrivileged(new PriviAction<String>(
061: "manifest.read.encoding")); //$NON-NLS-1$
062: if ("".equals(encoding)) { //$NON-NLS-1$
063: encoding = null;
064: }
065:
066: Attributes current = main;
067: ArrayList<String> list = new ArrayList<String>();
068:
069: // Return the chunk of main attributes in the manifest.
070: mainAttributesChunk = nextChunk(is, list);
071:
072: Iterator<String> it = list.iterator();
073: while (it.hasNext()) {
074: addAttribute(it.next(), current);
075: }
076:
077: // Check for version attribute
078: if (verString != null && main.getValue(verString) == null) {
079: throw new IOException(Messages.getString(
080: "archive.2D", verString)); //$NON-NLS-1$
081: }
082:
083: list.clear();
084: byte[] chunk = null;
085: while (chunks == null ? readLines(is, list)
086: : (chunk = nextChunk(is, list)) != null) {
087: if (list.size() == 0) {
088: continue;
089: }
090: it = list.iterator();
091: String line = it.next();
092: if (line.length() < 6
093: || !Util.toASCIILowerCase(line.substring(0, 5))
094: .equals("name:")) { //$NON-NLS-1$
095: throw new IOException(Messages.getString("archive.23")); //$NON-NLS-1$
096: }
097: // Name: length required space char
098: String name = line.substring(6, line.length());
099: current = new Attributes(12);
100: if (chunks != null) {
101: chunks.put(name, chunk);
102: }
103: entries.put(name, current);
104: while (it.hasNext()) {
105: addAttribute(it.next(), current);
106: }
107: list.clear();
108: }
109:
110: }
111:
112: byte[] getMainAttributesChunk() {
113: return mainAttributesChunk;
114: }
115:
116: private void addLine(int length, List<String> lines)
117: throws IOException {
118: if (encoding != null) {
119: lines.add(new String(buffer, 0, length, encoding));
120: } else {
121: if (usingUTF8) {
122: try {
123: if (charbuf.length < length) {
124: charbuf = new char[length];
125: }
126: int start = skipFirstEmptyLines(0, buffer);
127: lines.add(Util.convertUTF8WithBuf(buffer, charbuf,
128: Math.min(Math.max(length - 1, 0), start),
129: length));
130: } catch (UTFDataFormatException e) {
131: usingUTF8 = false;
132: }
133: }
134: if (!usingUTF8) {
135: if (charbuf.length < length) {
136: charbuf = new char[length];
137: }
138: // If invalid UTF8, convert bytes to chars setting the upper
139: // bytes to zeros
140: int charOffset = 0;
141: int offset = 0;
142: for (int i = length; --i >= 0;) {
143: charbuf[charOffset++] = (char) (buffer[offset++] & 0xff);
144: }
145: int start = skipFirstEmptyLines(0, buffer);
146: lines.add(new String(charbuf, Math.min(Math.max(
147: length - 1, 0), start), length));
148: }
149: }
150: }
151:
152: private int skipFirstEmptyLines(int start, byte[] buf) {
153: int res = start;
154: if (buf.length > start) {
155: if ((char) buf[res] == '\r') {
156: res++;
157: if ((res < buf.length) && ((char) buf[res] == '\n')) {
158: res++;
159: return skipFirstEmptyLines(res, buf);
160: }
161: return res;
162: } else if ((char) buf[res] == '\n') {
163: res++;
164: return skipFirstEmptyLines(res, buf);
165: } else {
166: return res;
167: }
168: }
169: return res;
170: }
171:
172: private byte[] nextChunk(InputStream in, List<String> lines)
173: throws IOException {
174: if (inbufCount == -1) {
175: return null;
176: }
177: byte next;
178: int pos = 0;
179: boolean blankline = false, lastCr = false;
180: out.reset();
181: while (true) {
182: if (inbufPos == inbufCount) {
183: if ((inbufCount = in.read(inbuf)) == -1) {
184: if (out.size() == 0) {
185: return null;
186: }
187: if (blankline) {
188: addLine(pos, lines);
189: }
190: return out.toByteArray();
191: }
192: if (inbufCount == inbuf.length && in.available() == 0) {
193: /* archive.2E = "line too long" */
194: throw new IOException(Messages
195: .getString("archive.2E")); //$NON-NLS-1$
196: }
197: inbufPos = 0;
198: }
199: next = inbuf[inbufPos++];
200: if (lastCr) {
201: if (next != '\n') {
202: inbufPos--;
203: next = '\r';
204: } else {
205: if (out.size() == 0) {
206: continue;
207: }
208: out.write('\r');
209: }
210: lastCr = false;
211: } else if (next == '\r') {
212: lastCr = true;
213: continue;
214: }
215: if (blankline) {
216: if (next == ' ') {
217: out.write(next);
218: blankline = false;
219: continue;
220: }
221: addLine(pos, lines);
222: if (next == '\n') {
223: out.write(next);
224: return out.toByteArray();
225: }
226: pos = 0;
227: } else if (next == '\n') {
228: if (out.size() == 0) {
229: continue;
230: }
231: out.write(next);
232: blankline = true;
233: continue;
234: }
235: blankline = false;
236: out.write(next);
237: if (pos == buffer.length) {
238: byte[] newBuf = new byte[buffer.length * 2];
239: System.arraycopy(buffer, 0, newBuf, 0, buffer.length);
240: buffer = newBuf;
241: }
242: buffer[pos++] = next;
243: }
244: }
245:
246: private boolean readLines(InputStream in, List<String> lines)
247: throws IOException {
248: if (inbufCount == -1) {
249: return false;
250: }
251: byte next;
252: int pos = 0;
253: boolean blankline = false, lastCr = false;
254: while (true) {
255: if (inbufPos == inbufCount) {
256: if ((inbufCount = in.read(inbuf)) == -1) {
257: if (blankline) {
258: addLine(pos, lines);
259: }
260: return lines.size() != 0;
261: }
262: if (inbufCount == inbuf.length && in.available() == 0) {
263: /* archive.2E = "line too long" */
264: throw new IOException(Messages
265: .getString("archive.2E")); //$NON-NLS-1$
266: }
267: inbufPos = 0;
268: }
269: next = inbuf[inbufPos++];
270: if (lastCr) {
271: if (next != '\n') {
272: inbufPos--;
273: next = '\r';
274: }
275: lastCr = false;
276: } else if (next == '\r') {
277: lastCr = true;
278: continue;
279: }
280: if (blankline) {
281: if (next == ' ') {
282: blankline = false;
283: continue;
284: }
285: addLine(pos, lines);
286: if (next == '\n') {
287: return true;
288: }
289: pos = 0;
290: } else if (next == '\n') {
291: if (pos == 0 && lines.size() == 0) {
292: continue;
293: }
294: blankline = true;
295: continue;
296: }
297: blankline = false;
298: if (pos == buffer.length) {
299: byte[] newBuf = new byte[buffer.length * 2];
300: System.arraycopy(buffer, 0, newBuf, 0, buffer.length);
301: buffer = newBuf;
302: }
303: buffer[pos++] = next;
304: }
305: }
306:
307: /* Get the next attribute and add it */
308: private void addAttribute(String line, Attributes current)
309: throws IOException {
310: String header;
311: int hdrIdx = line.indexOf(':');
312: if (hdrIdx < 1) {
313: throw new IOException(Messages
314: .getString("archive.2F", line)); //$NON-NLS-1$
315: }
316: header = line.substring(0, hdrIdx);
317: Attributes.Name name = attributeNames.get(header);
318: if (name == null) {
319: try {
320: name = new Attributes.Name(header);
321: } catch (IllegalArgumentException e) {
322: throw new IOException(e.toString());
323: }
324: attributeNames.put(header, name);
325: }
326: if (hdrIdx + 1 >= line.length()
327: || line.charAt(hdrIdx + 1) != ' ') {
328: throw new IOException(Messages
329: .getString("archive.2F", line)); //$NON-NLS-1$
330: }
331: // +2 due to required SPACE char
332: current.put(name, line.substring(hdrIdx + 2, line.length()));
333: }
334: }
|