/******************************************************************************* * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors * 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: * Marc R. Hoffmann - initial API and implementation * *******************************************************************************/ package org.jacoco.core.internal.instr; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.Iterator; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.regex.Pattern; /** * Support class to filter entries from JARs related to signatures. */ public class SignatureRemover { private static final Pattern SIGNATURE_FILES = Pattern .compile("META-INF/[^/]*\\.SF|" // + "META-INF/[^/]*\\.DSA|" // + "META-INF/[^/]*\\.RSA|" // + "META-INF/SIG-[^/]*"); private static final String MANIFEST_MF = "META-INF/MANIFEST.MF"; private static final String DIGEST_SUFFIX = "-Digest"; private boolean active; /** * Creates a new remover which is active. */ public SignatureRemover() { active = true; } /** * Defines whether this remover should be active. If it is not active it * will not remove any entries. * * @param active * <code>true</code> if it should remove signature related * entries. */ public void setActive(final boolean active) { this.active = active; } /** * Checks whether a entry with the provided name should be ignored at all. * * @param name * path name of the entry in question * @return true is the entry should be ignored */ public boolean removeEntry(final String name) { return active && SIGNATURE_FILES.matcher(name).matches(); } /** * Filters the content of the entry with the provided name if necessary. * * @param name * path name of the entry in question * @param in * source for the element to filter * @param out * output for the filtered contents * @return <code>true</code> if the content was filtered * @throws IOException * if the content can't be read or written */ public boolean filterEntry(final String name, final InputStream in, final OutputStream out) throws IOException { if (!active || !MANIFEST_MF.equals(name)) { return false; } final Manifest mf = new Manifest(in); filterManifestEntry(mf.getEntries().values()); mf.write(out); return true; } private void filterManifestEntry(final Collection<Attributes> entry) { for (final Iterator<Attributes> i = entry.iterator(); i.hasNext();) { final Attributes attributes = i.next(); filterManifestEntryAttributes(attributes); if (attributes.isEmpty()) { i.remove(); } } } private void filterManifestEntryAttributes(final Attributes attrs) { for (final Iterator<Object> i = attrs.keySet().iterator(); i.hasNext();) { if (String.valueOf(i.next()).endsWith(DIGEST_SUFFIX)) { i.remove(); } } } }