org.gradle.api.java.archives.internal.DefaultManifest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2007 the original author or authors.
*
* Licensed 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 org.gradle.api.java.archives.internal;
import groovy.lang.Closure;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Action;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.java.archives.Attributes;
import org.gradle.api.java.archives.ManifestMergeSpec;
import org.gradle.internal.IoActions;
import org.gradle.internal.file.PathToFileResolver;
import org.gradle.util.ConfigureUtil;
import org.gradle.util.SingleMessageLogger;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.Manifest;
public class DefaultManifest implements ManifestInternal {
public static final String DEFAULT_CONTENT_CHARSET = "UTF-8";
private List manifestMergeSpecs = new ArrayList();
private DefaultAttributes attributes = new DefaultAttributes();
private Map sections = new LinkedHashMap();
private PathToFileResolver fileResolver;
private String contentCharset;
public DefaultManifest(PathToFileResolver fileResolver) {
this(fileResolver, DEFAULT_CONTENT_CHARSET);
}
public DefaultManifest(PathToFileResolver fileResolver, String contentCharset) {
this.fileResolver = fileResolver;
this.contentCharset = contentCharset;
init();
}
public DefaultManifest(Object manifestPath, PathToFileResolver fileResolver) {
this(manifestPath, fileResolver, DEFAULT_CONTENT_CHARSET);
}
public DefaultManifest(Object manifestPath, PathToFileResolver fileResolver, String contentCharset) {
this.fileResolver = fileResolver;
this.contentCharset = contentCharset;
read(manifestPath);
}
private void init() {
getAttributes().put("Manifest-Version", "1.0");
}
@Override
public String getContentCharset() {
return contentCharset;
}
@Override
public void setContentCharset(String contentCharset) {
if (contentCharset == null) {
throw new InvalidUserDataException("contentCharset must not be null");
}
if (!Charset.isSupported(contentCharset)) {
throw new InvalidUserDataException(String.format("Charset for contentCharset '%s' is not supported by your JVM", contentCharset));
}
this.contentCharset = contentCharset;
}
public DefaultManifest mainAttributes(Map attributes) {
return attributes(attributes);
}
@Override
public DefaultManifest attributes(Map attributes) {
getAttributes().putAll(attributes);
return this;
}
@Override
public DefaultManifest attributes(Map attributes, String sectionName) {
if (!sections.containsKey(sectionName)) {
sections.put(sectionName, new DefaultAttributes());
}
sections.get(sectionName).putAll(attributes);
return this;
}
@Override
public Attributes getAttributes() {
return attributes;
}
@Override
public Map getSections() {
return sections;
}
public DefaultManifest clear() {
attributes.clear();
sections.clear();
manifestMergeSpecs.clear();
init();
return this;
}
static Manifest generateJavaManifest(org.gradle.api.java.archives.Manifest gradleManifest) {
Manifest javaManifest = new Manifest();
addMainAttributesToJavaManifest(gradleManifest, javaManifest);
addSectionAttributesToJavaManifest(gradleManifest, javaManifest);
return javaManifest;
}
private static void addMainAttributesToJavaManifest(org.gradle.api.java.archives.Manifest gradleManifest, Manifest javaManifest) {
for (Map.Entry entry : gradleManifest.getAttributes().entrySet()) {
String mainAttributeName = entry.getKey();
String mainAttributeValue = entry.getValue().toString();
javaManifest.getMainAttributes().putValue(mainAttributeName, mainAttributeValue);
}
}
private static void addSectionAttributesToJavaManifest(org.gradle.api.java.archives.Manifest gradleManifest, Manifest javaManifest) {
for (Map.Entry entry : gradleManifest.getSections().entrySet()) {
String sectionName = entry.getKey();
java.util.jar.Attributes sectionAttributes = new java.util.jar.Attributes();
for (Map.Entry attribute : entry.getValue().entrySet()) {
String attributeName = attribute.getKey();
String attributeValue = attribute.getValue().toString();
sectionAttributes.putValue(attributeName, attributeValue);
}
javaManifest.getEntries().put(sectionName, sectionAttributes);
}
}
@Override
public DefaultManifest from(Object... mergePaths) {
from(mergePaths, null);
return this;
}
@Override
public DefaultManifest from(Object mergePaths, Closure> closure) {
DefaultManifestMergeSpec mergeSpec = new DefaultManifestMergeSpec();
mergeSpec.from(mergePaths);
manifestMergeSpecs.add(mergeSpec);
ConfigureUtil.configure(closure, mergeSpec);
return this;
}
@Override
public DefaultManifest getEffectiveManifest() {
return getEffectiveManifestInternal(this);
}
protected DefaultManifest getEffectiveManifestInternal(DefaultManifest baseManifest) {
DefaultManifest resultManifest = baseManifest;
for (ManifestMergeSpec manifestMergeSpec : manifestMergeSpecs) {
resultManifest = ((DefaultManifestMergeSpec) manifestMergeSpec).merge(resultManifest, fileResolver);
}
return resultManifest;
}
@Deprecated
@Override
public DefaultManifest writeTo(Writer writer) {
SingleMessageLogger.nagUserOfDeprecated("Manifest.writeTo(Writer)", "Please use Manifest.writeTo(Object) instead");
try {
Manifest javaManifest = generateJavaManifest(getEffectiveManifest());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
javaManifest.write(buffer);
String manifestContent = buffer.toString(DEFAULT_CONTENT_CHARSET);
if (!DEFAULT_CONTENT_CHARSET.equals(contentCharset)) {
// Convert the UTF-8 manifest bytes to the requested content charset
manifestContent = new String(manifestContent.getBytes(contentCharset), contentCharset);
}
writer.write(manifestContent);
writer.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return this;
}
@Override
public org.gradle.api.java.archives.Manifest writeTo(OutputStream outputStream) {
writeTo(this, outputStream, contentCharset);
return this;
}
static void writeTo(org.gradle.api.java.archives.Manifest manifest, OutputStream outputStream, String contentCharset) {
try {
Manifest javaManifest = generateJavaManifest(manifest.getEffectiveManifest());
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
javaManifest.write(buffer);
byte[] manifestBytes;
if (DEFAULT_CONTENT_CHARSET.equals(contentCharset)) {
manifestBytes = buffer.toByteArray();
} else {
// Convert the UTF-8 manifest bytes to the requested content charset
manifestBytes = buffer.toString(DEFAULT_CONTENT_CHARSET).getBytes(contentCharset);
}
outputStream.write(manifestBytes);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public org.gradle.api.java.archives.Manifest writeTo(Object path) {
File manifestFile = fileResolver.resolve(path);
try {
File parentFile = manifestFile.getParentFile();
if (parentFile != null) {
FileUtils.forceMkdir(parentFile);
}
IoActions.withResource(new FileOutputStream(manifestFile), new Action() {
@Override
public void execute(FileOutputStream fileOutputStream) {
writeTo(fileOutputStream);
}
});
return this;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public List getMergeSpecs() {
return manifestMergeSpecs;
}
public boolean isEqualsTo(Object o) {
if (this == o) {
return true;
}
if (o == null || !(o instanceof DefaultManifest)) {
return false;
}
DefaultManifest effectiveThis = getEffectiveManifest();
DefaultManifest effectiveThat = ((DefaultManifest) o).getEffectiveManifest();
if (!effectiveThis.attributes.equals(effectiveThat.attributes)) {
return false;
}
if (!effectiveThis.sections.equals(effectiveThat.sections)) {
return false;
}
return true;
}
private void read(Object manifestPath) {
File manifestFile = fileResolver.resolve(manifestPath);
try {
byte[] manifestBytes = FileUtils.readFileToByteArray(manifestFile);
manifestBytes = prepareManifestBytesForInteroperability(manifestBytes);
// Eventually convert manifest content to UTF-8 before handing it to java.util.jar.Manifest
if (!DEFAULT_CONTENT_CHARSET.equals(contentCharset)) {
manifestBytes = new String(manifestBytes, contentCharset).getBytes(DEFAULT_CONTENT_CHARSET);
}
// Effectively read the manifest
Manifest javaManifest = new Manifest(new ByteArrayInputStream(manifestBytes));
addJavaManifestToAttributes(javaManifest);
addJavaManifestToSections(javaManifest);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Prepare Manifest bytes for interoperability. Ant Manifest class doesn't support split multi-bytes characters, Java Manifest class does. Ant Manifest class supports manifest sections starting
* without prior blank lines, Java Manifest class doesn't. Ant Manifest class supports manifest without last line blank, Java Manifest class doesn't. Therefore we need to insert blank lines before
* entries named 'Name' and before EOF if needed. This without decoding characters as this would break split multi-bytes characters, hence working on the bytes level.
*/
private byte[] prepareManifestBytesForInteroperability(byte[] original) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
boolean useCarriageReturns = false;
byte carriageReturn = (byte) '\r';
byte newLine = (byte) '\n';
for (int idx = 0; idx < original.length; idx++) {
byte current = original[idx];
if (current == carriageReturn) {
useCarriageReturns = true;
}
if (idx == original.length - 1) {
// Always append a new line at EOF
output.write(current);
if (useCarriageReturns) {
output.write(carriageReturn);
}
output.write(newLine);
} else if (current == newLine && idx + 5 < original.length) {
// Eventually add blank line before section
output.write(current);
if ((original[idx + 1] == 'N' || original[idx + 1] == 'n')
&& (original[idx + 2] == 'A' || original[idx + 2] == 'a')
&& (original[idx + 3] == 'M' || original[idx + 3] == 'm')
&& (original[idx + 4] == 'E' || original[idx + 4] == 'e')
&& (original[idx + 5] == ':')) {
if (useCarriageReturns) {
output.write(carriageReturn);
}
output.write(newLine);
}
} else {
output.write(current);
}
}
return output.toByteArray();
}
private void addJavaManifestToAttributes(Manifest javaManifest) {
attributes.put("Manifest-Version", "1.0");
for (Object attributeKey : javaManifest.getMainAttributes().keySet()) {
String attributeName = attributeKey.toString();
String attributeValue = javaManifest.getMainAttributes().getValue(attributeName);
attributes.put(attributeName, attributeValue);
}
}
private void addJavaManifestToSections(Manifest javaManifest) {
for (Map.Entry sectionEntry : javaManifest.getEntries().entrySet()) {
String sectionName = sectionEntry.getKey();
DefaultAttributes sectionAttributes = new DefaultAttributes();
for (Object attributeKey : sectionEntry.getValue().keySet()) {
String attributeName = attributeKey.toString();
String attributeValue = sectionEntry.getValue().getValue(attributeName);
sectionAttributes.put(attributeName, attributeValue);
}
sections.put(sectionName, sectionAttributes);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy