/******************************************************************************* * Copyright (c) 2001, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jst.common.internal.modulecore.util; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.Map; import java.util.jar.Attributes; import com.ibm.icu.util.StringTokenizer; /** * Helper class for manifest files */ public class ArchiveManifestImpl extends java.util.jar.Manifest implements ArchiveManifest { /** * ArchiveManifest constructor comment. */ public ArchiveManifestImpl() { super(); } /** * ArchiveManifest constructor comment. * * @param is * java.io.InputStream * @throws java.io.IOException * The exception description. */ public ArchiveManifestImpl(java.io.InputStream is) throws java.io.IOException { try { read(is); } catch(Exception e){ IOException ioe = new IOException(e.toString()); ioe.initCause(e); throw ioe; } } /** * ArchiveManifest constructor comment. * * @param man * java.util.jar.Manifest */ public ArchiveManifestImpl(java.util.jar.Manifest man) { super(man); } /** * Creates a new manifest entry (attributes) for the given name */ public void addEntry(String entryName) { Attributes attr = new Attributes(); addEntry(entryName, attr); } public void addEntry(String entryName, Attributes attr) { getEntries().put(entryName, attr); } /** * Adds the key/value pair to the attributes for the given entry name; if the entry does not * exist, creates a new attributes */ public void addEntryAttribute(String entryName, String key, String value) { Attributes attr = getAttributes(entryName); if (attr == null) addEntry(entryName); attr = getAttributes(entryName); attr.putValue(key, value); } public void addVersionIfNecessary() { //This is a hack because of the fact that the manifest does not serialize correctly if //The version is not set. In addition to saves, the serialization is used for copy if (getManifestVersion() == null || getManifestVersion().equals(""))//$NON-NLS-1$ setManifestVersion("1.0");//$NON-NLS-1$ } /** * @see org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveManifest */ public void appendClassPath(java.lang.String extension) { String classPath = getClassPath(); if (classPath != null) setClassPath(classPath + " " + extension);//$NON-NLS-1$ else setClassPath(extension); } /** * @see org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveManifest */ public java.lang.String getClassPath() { return ManifestUtilities.getValueIgnoreKeyCase(Attributes.Name.CLASS_PATH.toString(), getMainAttributes()); } /** * @see org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveManifest */ public java.lang.String[] getClassPathTokenized() { String classPath = getClassPath(); if (classPath == null) return new String[0]; return ManifestUtilities.getTokens(classPath); } public String getEntryAttribute(String entryName, String key) { Attributes attr = getAttributes(entryName); if (attr == null) return null; return attr.getValue(key); } public String getMainClass() { return ManifestUtilities.getValueIgnoreKeyCase(Attributes.Name.MAIN_CLASS.toString(), getMainAttributes()); } public String getManifestVersion() { return getMainAttributes().getValue(Attributes.Name.MANIFEST_VERSION); } /** * @see org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveManifest */ public void mergeClassPath(java.lang.String[] classPathEntries) { StringBuffer sb = new StringBuffer(); java.util.List existing = java.util.Arrays.asList(getClassPathTokenized()); String cp = getClassPath(); if (cp != null) sb.append(cp); boolean empty = cp == null || "".equals(cp); //$NON-NLS-1$ for (int i = 0; i < classPathEntries.length; i++) { if (!existing.contains(classPathEntries[i])) { if (!empty) sb.append(" "); //$NON-NLS-1$ else empty = false; sb.append(classPathEntries[i]); } } setClassPath(sb.toString()); } public void removeEntry(String entryName) { getEntries().remove(entryName); } public void removeEntryAttribute(String entryName, Object key) { Attributes attr = getAttributes(entryName); if (attr != null) attr.remove(key); } /** * @see org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveManifest */ public void setClassPath(java.lang.String aSpaceDelimitedPath) { Attributes attributes = getMainAttributes(); if (aSpaceDelimitedPath == null) attributes.remove(Attributes.Name.CLASS_PATH); else attributes.putValue(Attributes.Name.CLASS_PATH.toString(), aSpaceDelimitedPath); } /** * @see org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveManifest */ public void setMainClass(java.lang.String className) { Attributes attributes = getMainAttributes(); if (className == null) attributes.remove(Attributes.Name.MAIN_CLASS); else attributes.putValue(Attributes.Name.MAIN_CLASS.toString(), className); } /** * @see org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveManifest */ public void setManifestVersion(java.lang.String version) { Attributes attributes = getMainAttributes(); attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), version); } /** * Writes the Manifest to the specified OutputStream, splitting each classpath entry on a line * by itself. * * @param out * the output stream * @exception IOException * if an I/O error has occurred */ public void writeSplittingClasspath(OutputStream out) throws IOException { DataOutputStream dos = new DataOutputStream(out); // Write out the main attributes for the manifest writeMainSplittingClasspath(getMainAttributes(), dos); // Now write out the pre-entry attributes Iterator it = getEntries().entrySet().iterator(); while (it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); StringBuffer buffer = new StringBuffer("Name: "); //$NON-NLS-1$ buffer.append((String) e.getKey()); buffer.append("\r\n"); //$NON-NLS-1$ localMake72Safe(buffer); dos.writeBytes(buffer.toString()); write((Attributes) e.getValue(), dos); } dos.flush(); } /* * Writes the current attributes to the specified data output stream. XXX Need to handle UTF8 * values and break up lines longer than 72 bytes * * @see Attributes#write */ protected void write(Attributes attributes, DataOutputStream os) throws IOException { Iterator it = attributes.entrySet().iterator(); while (it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); StringBuffer buffer = new StringBuffer(((Attributes.Name) e.getKey()).toString()); buffer.append(": "); //$NON-NLS-1$ buffer.append(toUtf8((String) e.getValue())); buffer.append("\r\n"); //$NON-NLS-1$ localMake72Safe(buffer); os.writeBytes(buffer.toString()); } os.writeBytes("\r\n"); //$NON-NLS-1$ } /* * Writes the current attributes to the specified data output stream, make sure to write out the * MANIFEST_VERSION or SIGNATURE_VERSION attributes first. * * @see Attributes#writeMain */ protected void writeMainSplittingClasspath(Attributes attributes, DataOutputStream out) throws IOException { // write out the *-Version header first, if it exists String vername = Attributes.Name.MANIFEST_VERSION.toString(); String version = toUtf8(attributes.getValue(vername)); if (version == null) { vername = Attributes.Name.SIGNATURE_VERSION.toString(); version = toUtf8(attributes.getValue(vername)); } if (version != null) { out.writeBytes(vername + ": " + version + "\r\n"); //$NON-NLS-1$ //$NON-NLS-2$ } // write out all attributes except for the version // we wrote out earlier Iterator it = attributes.entrySet().iterator(); while (it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); String name = ((Attributes.Name) e.getKey()).toString(); if ((version != null) && !(name.equalsIgnoreCase(vername))) { if (name.equalsIgnoreCase(Attributes.Name.CLASS_PATH.toString())) { writeSplit(out, name, toUtf8((String) e.getValue())); continue; } StringBuffer buffer = new StringBuffer(name); buffer.append(": "); //$NON-NLS-1$ buffer.append(toUtf8((String) e.getValue())); buffer.append("\r\n"); //$NON-NLS-1$ localMake72Safe(buffer); out.writeBytes(buffer.toString()); } } out.writeBytes("\r\n"); //$NON-NLS-1$ } protected void writeSplit(DataOutputStream out, String name, String value) throws IOException { StringTokenizer tok = new StringTokenizer(value); int inc = 0; while (tok.hasMoreTokens()) { StringBuffer buffer = null; if (inc == 0) { buffer = new StringBuffer(name); buffer.append(": "); //$NON-NLS-1$ } else { buffer = new StringBuffer(); buffer.append(' '); } buffer.append(tok.nextToken()); if (tok.countTokens() > 0) buffer.append(" \r\n"); //$NON-NLS-1$ else buffer.append("\r\n"); //$NON-NLS-1$ localMake72Safe(buffer); out.writeBytes(buffer.toString()); inc++; } } /** * Adds line breaks to enforce a maximum 72 bytes per line. */ protected static void localMake72Safe(StringBuffer line) { int length = line.length(); if (length > 72) { int index = 70; // bug 233801 - we are splitting every 72 bytes, but adding two bytes to the end of, // and one space at the beginning of every line after the first while (index - 1 < length) { if (line.charAt(index) == ' ') { index--; } line.insert(index, "\r\n "); //$NON-NLS-1$ index += 72; length += 3; } } return; } public String getImplementationVersion() { return getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); } public void setImplemenationVersion(String version) { Attributes attributes = getMainAttributes(); attributes.putValue(Attributes.Name.IMPLEMENTATION_VERSION.toString(), version); } /** * Encodes a double-byte string into UTF8 form. Every character in the * returned string represents one byte of the UTF8 encoding. */ private String toUtf8( final String str ) throws IOException { if( str == null ) { return null; } final byte[] utf8 = str.getBytes( "UTF-8" ); //$NON-NLS-1$ return new String( utf8, 0, 0, utf8.length ); } }