| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.util.jar; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.CharBuffer; |
| import java.nio.charset.CharsetDecoder; |
| import java.nio.charset.CoderResult; |
| import java.util.Map; |
| |
| import org.apache.harmony.archive.internal.nls.Messages; |
| import org.apache.harmony.luni.util.ThreadLocalCache; |
| |
| class InitManifest { |
| |
| private byte[] buf; |
| |
| private int pos; |
| |
| Attributes.Name name; |
| |
| String value; |
| |
| CharsetDecoder decoder = ThreadLocalCache.utf8Decoder.get(); |
| CharBuffer cBuf = ThreadLocalCache.charBuffer.get(); |
| |
| InitManifest(byte[] buf, Attributes main, Attributes.Name ver) |
| throws IOException { |
| |
| this.buf = buf; |
| |
| // check a version attribute |
| if (!readHeader() || (ver != null && !name.equals(ver))) { |
| throw new IOException(Messages.getString( |
| "archive.2D", ver)); //$NON-NLS-1$ |
| } |
| |
| main.put(name, value); |
| while (readHeader()) { |
| main.put(name, value); |
| } |
| } |
| |
| void initEntries(Map<String, Attributes> entries, |
| Map<String, Manifest.Chunk> chunks) throws IOException { |
| |
| int mark = pos; |
| while (readHeader()) { |
| if (!Attributes.Name.NAME.equals(name)) { |
| throw new IOException(Messages.getString("archive.23")); //$NON-NLS-1$ |
| } |
| String entryNameValue = value; |
| |
| Attributes entry = entries.get(entryNameValue); |
| if (entry == null) { |
| entry = new Attributes(12); |
| } |
| |
| while (readHeader()) { |
| entry.put(name, value); |
| } |
| |
| if (chunks != null) { |
| if (chunks.get(entryNameValue) != null) { |
| // TODO A bug: there might be several verification chunks for |
| // the same name. I believe they should be used to update |
| // signature in order of appearance; there are two ways to fix |
| // this: either use a list of chunks, or decide on used |
| // signature algorithm in advance and reread the chunks while |
| // updating the signature; for now a defensive error is thrown |
| throw new IOException(Messages.getString("archive.34")); //$NON-NLS-1$ |
| } |
| chunks.put(entryNameValue, new Manifest.Chunk(mark, pos)); |
| mark = pos; |
| } |
| |
| entries.put(entryNameValue, entry); |
| } |
| } |
| |
| int getPos() { |
| return pos; |
| } |
| |
| /** |
| * Number of subsequent line breaks. |
| */ |
| int linebreak = 0; |
| |
| /** |
| * Read a single line from the manifest buffer. |
| */ |
| private boolean readHeader() throws IOException { |
| if (linebreak > 1) { |
| // break a section on an empty line |
| linebreak = 0; |
| return false; |
| } |
| readName(); |
| linebreak = 0; |
| readValue(); |
| // if the last line break is missed, the line |
| // is ignored by the reference implementation |
| return linebreak > 0; |
| } |
| |
| private byte[] wrap(int mark, int pos) { |
| byte[] buffer = new byte[pos - mark]; |
| System.arraycopy(buf, mark, buffer, 0, pos - mark); |
| return buffer; |
| } |
| |
| private void readName() throws IOException { |
| int i = 0; |
| int mark = pos; |
| |
| while (pos < buf.length) { |
| byte b = buf[pos++]; |
| |
| if (b == ':') { |
| byte[] nameBuffer = wrap(mark, pos - 1); |
| |
| if (buf[pos++] != ' ') { |
| throw new IOException(Messages.getString( |
| "archive.30", nameBuffer)); //$NON-NLS-1$ |
| } |
| |
| name = new Attributes.Name(nameBuffer); |
| return; |
| } |
| |
| if (!((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' |
| || b == '-' || (b >= '0' && b <= '9'))) { |
| throw new IOException(Messages.getString("archive.30", b)); //$NON-NLS-1$ |
| } |
| } |
| if (i > 0) { |
| throw new IOException(Messages.getString( |
| "archive.30", wrap(mark, buf.length))); //$NON-NLS-1$ |
| } |
| } |
| |
| private void readValue() throws IOException { |
| byte next; |
| boolean lastCr = false; |
| int mark = pos; |
| int last = pos; |
| |
| decoder.reset(); |
| cBuf.clear(); |
| |
| while (pos < buf.length) { |
| next = buf[pos++]; |
| |
| switch (next) { |
| case 0: |
| throw new IOException(Messages.getString("archive.2F")); //$NON-NLS-1$ |
| case '\n': |
| if (lastCr) { |
| lastCr = false; |
| } else { |
| linebreak++; |
| } |
| continue; |
| case '\r': |
| lastCr = true; |
| linebreak++; |
| continue; |
| case ' ': |
| if (linebreak == 1) { |
| decode(mark, last, false); |
| mark = pos; |
| linebreak = 0; |
| continue; |
| } |
| } |
| |
| if (linebreak >= 1) { |
| pos--; |
| break; |
| } |
| last = pos; |
| } |
| |
| decode(mark, last, true); |
| while (CoderResult.OVERFLOW == decoder.flush(cBuf)) { |
| enlargeBuffer(); |
| } |
| value = new String(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); |
| } |
| |
| private void decode(int mark, int pos, boolean endOfInput) |
| throws IOException { |
| ByteBuffer bBuf = ByteBuffer.wrap(buf, mark, pos - mark); |
| while (CoderResult.OVERFLOW == decoder.decode(bBuf, cBuf, endOfInput)) { |
| enlargeBuffer(); |
| } |
| } |
| |
| private void enlargeBuffer() { |
| CharBuffer newBuf = CharBuffer.allocate(cBuf.capacity() * 2); |
| newBuf.put(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); |
| cBuf = newBuf; |
| ThreadLocalCache.charBuffer.set(cBuf); |
| } |
| } |