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.IOException;
021: import java.io.InputStream;
022: import java.io.OutputStream;
023: import java.nio.CharBuffer;
024: import java.nio.charset.Charset;
025: import java.security.AccessController;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import java.util.Map;
029:
030: import org.apache.harmony.luni.util.PriviAction;
031:
032: /**
033: * The Manifest class is used to obtain attribute information for a JarFile and
034: * its entries.
035: *
036: */
037: public class Manifest implements Cloneable {
038: private static final int LINE_LENGTH_LIMIT = 70;
039:
040: private static final byte[] LINE_SEPARATOR = new byte[] { '\r',
041: '\n' };
042:
043: private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name(
044: "Name"); //$NON-NLS-1$
045:
046: private Attributes mainAttributes = new Attributes();
047:
048: private HashMap<String, Attributes> entryAttributes = new HashMap<String, Attributes>();
049:
050: private HashMap<String, byte[]> chunks;
051:
052: /**
053: * The data chunk of Main Attributes in the manifest is needed in
054: * verification.
055: */
056: private byte[] mainAttributesChunk;
057:
058: /**
059: * Constructs a new Manifest instance.
060: */
061: public Manifest() {
062: super ();
063: }
064:
065: /**
066: * Constructs a new Manifest instance using the attributes obtained from is.
067: *
068: * @param is
069: * InputStream to parse for attributes
070: *
071: * @throws IOException
072: * if an IO error occurs while creating this Manifest
073: *
074: */
075: public Manifest(InputStream is) throws IOException {
076: super ();
077: read(is);
078: }
079:
080: /**
081: * Constructs a new Manifest instance. The new instance will have the same
082: * attributes as those found in the parameter Manifest.
083: *
084: * @param man
085: * Manifest instance to obtain attributes from
086: */
087: @SuppressWarnings("unchecked")
088: public Manifest(Manifest man) {
089: mainAttributes = (Attributes) man.mainAttributes.clone();
090: entryAttributes = (HashMap<String, Attributes>) man.entryAttributes
091: .clone();
092: }
093:
094: Manifest(InputStream is, boolean readChunks) throws IOException {
095: if (readChunks) {
096: chunks = new HashMap<String, byte[]>();
097: }
098: read(is);
099: }
100:
101: /**
102: * Resets the both the mainAttributes as well as the entry Attributes
103: * associated with this Manifest.
104: */
105: public void clear() {
106: entryAttributes.clear();
107: mainAttributes.clear();
108: }
109:
110: /**
111: * Returns the Attributes associated with the parameter entry name
112: *
113: * @param name
114: * The name of the entry to obtain Attributes for.
115: * @return The Attributes for the entry or null if the entry does not exist.
116: */
117: public Attributes getAttributes(String name) {
118: return getEntries().get(name);
119: }
120:
121: /**
122: * Returns a Map containing the Attributes for each entry in the Manifest.
123: *
124: * @return A Map of entry attributes
125: */
126: public Map<String, Attributes> getEntries() {
127: return entryAttributes;
128: }
129:
130: /**
131: * Returns the main Attributes of the JarFile.
132: *
133: * @return Main Attributes associated with the source JarFile
134: */
135: public Attributes getMainAttributes() {
136: return mainAttributes;
137: }
138:
139: /**
140: * Creates a copy of this Manifest. The returned Manifest will equal the
141: * Manifest from which it was cloned.
142: *
143: * @return A copy of the receiver.
144: */
145: @Override
146: public Object clone() {
147: return new Manifest(this );
148: }
149:
150: /**
151: * Writes out the attribute information of the receiver to the specified
152: * OutputStream
153: *
154: * @param os
155: * The OutputStream to write to.
156: *
157: * @throws IOException
158: * If an error occurs writing the Manifest
159: */
160: public void write(OutputStream os) throws IOException {
161: write(this , os);
162: }
163:
164: /**
165: * Constructs a new Manifest instance obtaining Attribute information from
166: * the parameter InputStream.
167: *
168: * @param is
169: * The InputStream to read from
170: * @throws IOException
171: * If an error occurs reading the Manifest.
172: */
173: public void read(InputStream is) throws IOException {
174: InitManifest initManifest = new InitManifest(is,
175: mainAttributes, entryAttributes, chunks, null);
176: mainAttributesChunk = initManifest.getMainAttributesChunk();
177: }
178:
179: /**
180: * Returns the hashCode for this instance.
181: *
182: * @return This Manifest's hashCode
183: */
184: @Override
185: public int hashCode() {
186: return mainAttributes.hashCode() ^ entryAttributes.hashCode();
187: }
188:
189: /**
190: * Determines if the receiver is equal to the parameter Object. Two
191: * Manifests are equal if they have identical main Attributes as well as
192: * identical entry Attributes.
193: *
194: * @param o
195: * The Object to compare against.
196: * @return <code>true</code> if the manifests are equal,
197: * <code>false</code> otherwise
198: */
199: @Override
200: public boolean equals(Object o) {
201: if (o == null) {
202: return false;
203: }
204: if (o.getClass() != this .getClass()) {
205: return false;
206: }
207: if (!mainAttributes.equals(((Manifest) o).mainAttributes)) {
208: return false;
209: }
210: return entryAttributes.equals(((Manifest) o).entryAttributes);
211: }
212:
213: byte[] getChunk(String name) {
214: return chunks.get(name);
215: }
216:
217: void removeChunks() {
218: chunks = null;
219: }
220:
221: byte[] getMainAttributesChunk() {
222: return mainAttributesChunk;
223: }
224:
225: /**
226: * Writes out the attribute information of the receiver to the specified
227: * OutputStream
228: *
229: * @param manifest
230: * the attribute information of the receiver
231: * @param out
232: * The OutputStream to write to.
233: *
234: * @throws IOException
235: * If an error occurs writing the Manifest
236: */
237: static void write(Manifest manifest, OutputStream out)
238: throws IOException {
239: Charset charset = null;
240: String encoding = AccessController
241: .doPrivileged(new PriviAction<String>(
242: "manifest.write.encoding")); //$NON-NLS-1$
243: if (encoding != null) {
244: if (encoding.length() == 0) {
245: encoding = "UTF8"; //$NON-NLS-1$
246: }
247: charset = Charset.forName(encoding);
248: }
249: String version = manifest.mainAttributes
250: .getValue(Attributes.Name.MANIFEST_VERSION);
251: if (version != null) {
252: writeEntry(out, charset, Attributes.Name.MANIFEST_VERSION,
253: version);
254: Iterator<?> entries = manifest.mainAttributes.keySet()
255: .iterator();
256: while (entries.hasNext()) {
257: Attributes.Name name = (Attributes.Name) entries.next();
258: if (!name.equals(Attributes.Name.MANIFEST_VERSION)) {
259: writeEntry(out, charset, name,
260: manifest.mainAttributes.getValue(name));
261: }
262: }
263: }
264: out.write(LINE_SEPARATOR);
265: Iterator<String> i = manifest.entryAttributes.keySet()
266: .iterator();
267: while (i.hasNext()) {
268: String key = i.next();
269: writeEntry(out, charset, NAME_ATTRIBUTE, key);
270: Attributes attrib = manifest.entryAttributes.get(key);
271: Iterator<?> entries = attrib.keySet().iterator();
272: while (entries.hasNext()) {
273: Attributes.Name name = (Attributes.Name) entries.next();
274: writeEntry(out, charset, name, attrib.getValue(name));
275: }
276: out.write(LINE_SEPARATOR);
277: }
278: }
279:
280: private static void writeEntry(OutputStream os, Charset charset,
281: Attributes.Name name, String value) throws IOException {
282: int offset = 0;
283: int limit = LINE_LENGTH_LIMIT;
284: byte[] out = (name.toString() + ": ").getBytes("ISO8859_1"); //$NON-NLS-1$ //$NON-NLS-2$
285: if (out.length > limit) {
286: while (out.length - offset >= limit) {
287: int len = out.length - offset;
288: if (len > limit) {
289: len = limit;
290: }
291: if (offset > 0) {
292: os.write(' ');
293: }
294: os.write(out, offset, len);
295: os.write(LINE_SEPARATOR);
296: offset += len;
297: limit = LINE_LENGTH_LIMIT - 1;
298: }
299: }
300: int size = out.length - offset;
301: final byte[] outBuf = new byte[LINE_LENGTH_LIMIT];
302: System.arraycopy(out, offset, outBuf, 0, size);
303: for (int i = 0; i < value.length(); i++) {
304: char[] oneChar = new char[1];
305: oneChar[0] = value.charAt(i);
306: byte[] buf;
307: if (oneChar[0] < 128 || charset == null) {
308: byte[] oneByte = new byte[1];
309: oneByte[0] = (byte) oneChar[0];
310: buf = oneByte;
311: } else {
312: buf = charset.encode(CharBuffer.wrap(oneChar, 0, 1))
313: .array();
314: }
315: if (size + buf.length > limit) {
316: if (limit != LINE_LENGTH_LIMIT) {
317: os.write(' ');
318: }
319: os.write(outBuf, 0, size);
320: os.write(LINE_SEPARATOR);
321: limit = LINE_LENGTH_LIMIT - 1;
322: size = 0;
323: }
324: if (buf.length == 1) {
325: outBuf[size] = buf[0];
326: } else {
327: System.arraycopy(buf, 0, outBuf, size, buf.length);
328: }
329: size += buf.length;
330: }
331: if (size > 0) {
332: if (limit != LINE_LENGTH_LIMIT) {
333: os.write(' ');
334: }
335: os.write(outBuf, 0, size);
336: os.write(LINE_SEPARATOR);
337: }
338: }
339: }
|