1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.util.jar; 19 20 import java.io.ByteArrayInputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.OutputStream; 24 import java.lang.reflect.Field; 25 import java.nio.ByteBuffer; 26 import java.nio.CharBuffer; 27 import java.nio.charset.CharsetEncoder; 28 import java.nio.charset.Charsets; 29 import java.nio.charset.CoderResult; 30 import java.util.HashMap; 31 import java.util.Iterator; 32 import java.util.Map; 33 import libcore.io.Streams; 34 35 /** 36 * The {@code Manifest} class is used to obtain attribute information for a 37 * {@code JarFile} and its entries. 38 */ 39 public class Manifest implements Cloneable { 40 static final int LINE_LENGTH_LIMIT = 72; 41 42 private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' }; 43 44 private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' }; 45 46 private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name"); 47 48 private static final Field BAIS_BUF = getByteArrayInputStreamField("buf"); 49 private static final Field BAIS_POS = getByteArrayInputStreamField("pos"); 50 51 private static Field getByteArrayInputStreamField(String name) { 52 try { 53 Field f = ByteArrayInputStream.class.getDeclaredField(name); 54 f.setAccessible(true); 55 return f; 56 } catch (Exception ex) { 57 throw new AssertionError(ex); 58 } 59 } 60 61 private Attributes mainAttributes = new Attributes(); 62 63 private HashMap<String, Attributes> entries = new HashMap<String, Attributes>(); 64 65 static class Chunk { 66 int start; 67 int end; 68 69 Chunk(int start, int end) { 70 this.start = start; 71 this.end = end; 72 } 73 } 74 75 private HashMap<String, Chunk> chunks; 76 77 /** 78 * The end of the main attributes section in the manifest is needed in 79 * verification. 80 */ 81 private int mainEnd; 82 83 /** 84 * Creates a new {@code Manifest} instance. 85 */ 86 public Manifest() { 87 } 88 89 /** 90 * Creates a new {@code Manifest} instance using the attributes obtained 91 * from the input stream. 92 * 93 * @param is 94 * {@code InputStream} to parse for attributes. 95 * @throws IOException 96 * if an IO error occurs while creating this {@code Manifest} 97 */ 98 public Manifest(InputStream is) throws IOException { 99 read(is); 100 } 101 102 /** 103 * Creates a new {@code Manifest} instance. The new instance will have the 104 * same attributes as those found in the parameter {@code Manifest}. 105 * 106 * @param man 107 * {@code Manifest} instance to obtain attributes from. 108 */ 109 @SuppressWarnings("unchecked") 110 public Manifest(Manifest man) { 111 mainAttributes = (Attributes) man.mainAttributes.clone(); 112 entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man 113 .getEntries()).clone(); 114 } 115 116 Manifest(InputStream is, boolean readChunks) throws IOException { 117 if (readChunks) { 118 chunks = new HashMap<String, Chunk>(); 119 } 120 read(is); 121 } 122 123 /** 124 * Resets the both the main attributes as well as the entry attributes 125 * associated with this {@code Manifest}. 126 */ 127 public void clear() { 128 entries.clear(); 129 mainAttributes.clear(); 130 } 131 132 /** 133 * Returns the {@code Attributes} associated with the parameter entry 134 * {@code name}. 135 * 136 * @param name 137 * the name of the entry to obtain {@code Attributes} from. 138 * @return the Attributes for the entry or {@code null} if the entry does 139 * not exist. 140 */ 141 public Attributes getAttributes(String name) { 142 return getEntries().get(name); 143 } 144 145 /** 146 * Returns a map containing the {@code Attributes} for each entry in the 147 * {@code Manifest}. 148 * 149 * @return the map of entry attributes. 150 */ 151 public Map<String, Attributes> getEntries() { 152 return entries; 153 } 154 155 /** 156 * Returns the main {@code Attributes} of the {@code JarFile}. 157 * 158 * @return main {@code Attributes} associated with the source {@code 159 * JarFile}. 160 */ 161 public Attributes getMainAttributes() { 162 return mainAttributes; 163 } 164 165 /** 166 * Creates a copy of this {@code Manifest}. The returned {@code Manifest} 167 * will equal the {@code Manifest} from which it was cloned. 168 * 169 * @return a copy of this instance. 170 */ 171 @Override 172 public Object clone() { 173 return new Manifest(this); 174 } 175 176 /** 177 * Writes out the attribute information of the receiver to the specified 178 * {@code OutputStream}. 179 * 180 * @param os 181 * The {@code OutputStream} to write to. 182 * @throws IOException 183 * If an error occurs writing the {@code Manifest}. 184 */ 185 public void write(OutputStream os) throws IOException { 186 write(this, os); 187 } 188 189 /** 190 * Merges name/attribute pairs read from the input stream {@code is} into this manifest. 191 * 192 * @param is 193 * The {@code InputStream} to read from. 194 * @throws IOException 195 * If an error occurs reading the manifest. 196 */ 197 public void read(InputStream is) throws IOException { 198 byte[] buf; 199 if (is instanceof ByteArrayInputStream) { 200 buf = exposeByteArrayInputStreamBytes((ByteArrayInputStream) is); 201 } else { 202 buf = Streams.readFullyNoClose(is); 203 } 204 205 if (buf.length == 0) { 206 return; 207 } 208 209 // a workaround for HARMONY-5662 210 // replace EOF and NUL with another new line 211 // which does not trigger an error 212 byte b = buf[buf.length - 1]; 213 if (b == 0 || b == 26) { 214 buf[buf.length - 1] = '\n'; 215 } 216 217 // Attributes.Name.MANIFEST_VERSION is not used for 218 // the second parameter for RI compatibility 219 InitManifest im = new InitManifest(buf, mainAttributes, null); 220 mainEnd = im.getPos(); 221 im.initEntries(entries, chunks); 222 } 223 224 /** 225 * Returns a byte[] containing all the bytes from a ByteArrayInputStream. 226 * Where possible, this returns the actual array rather than a copy. 227 */ 228 private static byte[] exposeByteArrayInputStreamBytes(ByteArrayInputStream bais) { 229 byte[] buffer; 230 synchronized (bais) { 231 byte[] buf; 232 int pos; 233 try { 234 buf = (byte[]) BAIS_BUF.get(bais); 235 pos = BAIS_POS.getInt(bais); 236 } catch (IllegalAccessException iae) { 237 throw new AssertionError(iae); 238 } 239 int available = bais.available(); 240 if (pos == 0 && buf.length == available) { 241 buffer = buf; 242 } else { 243 buffer = new byte[available]; 244 System.arraycopy(buf, pos, buffer, 0, available); 245 } 246 bais.skip(available); 247 } 248 return buffer; 249 } 250 251 /** 252 * Returns the hash code for this instance. 253 * 254 * @return this {@code Manifest}'s hashCode. 255 */ 256 @Override 257 public int hashCode() { 258 return mainAttributes.hashCode() ^ getEntries().hashCode(); 259 } 260 261 /** 262 * Determines if the receiver is equal to the parameter object. Two {@code 263 * Manifest}s are equal if they have identical main attributes as well as 264 * identical entry attributes. 265 * 266 * @param o 267 * the object to compare against. 268 * @return {@code true} if the manifests are equal, {@code false} otherwise 269 */ 270 @Override 271 public boolean equals(Object o) { 272 if (o == null) { 273 return false; 274 } 275 if (o.getClass() != this.getClass()) { 276 return false; 277 } 278 if (!mainAttributes.equals(((Manifest) o).mainAttributes)) { 279 return false; 280 } 281 return getEntries().equals(((Manifest) o).getEntries()); 282 } 283 284 Chunk getChunk(String name) { 285 return chunks.get(name); 286 } 287 288 void removeChunks() { 289 chunks = null; 290 } 291 292 int getMainAttributesEnd() { 293 return mainEnd; 294 } 295 296 /** 297 * Writes out the attribute information of the specified manifest to the 298 * specified {@code OutputStream} 299 * 300 * @param manifest 301 * the manifest to write out. 302 * @param out 303 * The {@code OutputStream} to write to. 304 * @throws IOException 305 * If an error occurs writing the {@code Manifest}. 306 */ 307 static void write(Manifest manifest, OutputStream out) throws IOException { 308 CharsetEncoder encoder = Charsets.UTF_8.newEncoder(); 309 ByteBuffer buffer = ByteBuffer.allocate(LINE_LENGTH_LIMIT); 310 311 String version = manifest.mainAttributes.getValue(Attributes.Name.MANIFEST_VERSION); 312 if (version != null) { 313 writeEntry(out, Attributes.Name.MANIFEST_VERSION, version, encoder, buffer); 314 Iterator<?> entries = manifest.mainAttributes.keySet().iterator(); 315 while (entries.hasNext()) { 316 Attributes.Name name = (Attributes.Name) entries.next(); 317 if (!name.equals(Attributes.Name.MANIFEST_VERSION)) { 318 writeEntry(out, name, manifest.mainAttributes.getValue(name), encoder, buffer); 319 } 320 } 321 } 322 out.write(LINE_SEPARATOR); 323 Iterator<String> i = manifest.getEntries().keySet().iterator(); 324 while (i.hasNext()) { 325 String key = i.next(); 326 writeEntry(out, NAME_ATTRIBUTE, key, encoder, buffer); 327 Attributes attrib = manifest.entries.get(key); 328 Iterator<?> entries = attrib.keySet().iterator(); 329 while (entries.hasNext()) { 330 Attributes.Name name = (Attributes.Name) entries.next(); 331 writeEntry(out, name, attrib.getValue(name), encoder, buffer); 332 } 333 out.write(LINE_SEPARATOR); 334 } 335 } 336 337 private static void writeEntry(OutputStream os, Attributes.Name name, 338 String value, CharsetEncoder encoder, ByteBuffer bBuf) throws IOException { 339 String nameString = name.getName(); 340 os.write(nameString.getBytes(Charsets.US_ASCII)); 341 os.write(VALUE_SEPARATOR); 342 343 encoder.reset(); 344 bBuf.clear().limit(LINE_LENGTH_LIMIT - nameString.length() - 2); 345 346 CharBuffer cBuf = CharBuffer.wrap(value); 347 348 while (true) { 349 CoderResult r = encoder.encode(cBuf, bBuf, true); 350 if (CoderResult.UNDERFLOW == r) { 351 r = encoder.flush(bBuf); 352 } 353 os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position()); 354 os.write(LINE_SEPARATOR); 355 if (CoderResult.UNDERFLOW == r) { 356 break; 357 } 358 os.write(' '); 359 bBuf.clear().limit(LINE_LENGTH_LIMIT - 1); 360 } 361 } 362 } 363