diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt new file mode 100644 index 000000000..f28b8014c --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -0,0 +1,54 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.ByteArrayOutputStream +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * A resource processor that appends content for a resource, separated by a newline. + * + * Modified from `org.apache.maven.plugins.shade.resource.AppendingTransformer.java` + * + * Modifications + * @author John Engelman + */ +@CacheableTransformer +@Suppress("ktlint:standard:backing-property-naming") +open class AppendingTransformer : Transformer { + /** + * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). + */ + private var _data: ByteArrayOutputStream? = null + private inline val data get() = if (_data == null) ByteArrayOutputStream().also { _data = it } else _data!! + + @get:Optional + @get:Input + var resource: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + return resource.equals(element.relativePath.pathString, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + context.inputStream.use { + it.copyTo(data) + data.write('\n'.code) + } + } + + override fun hasTransformedResource(): Boolean { + return data.size() > 0 + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(requireNotNull(resource)) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + + data.toByteArray().inputStream().copyTo(os) + data.reset() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt new file mode 100644 index 000000000..e925cc08b --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -0,0 +1,74 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.util.jar.JarFile.MANIFEST_NAME +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.slf4j.LoggerFactory + +/** + * A resource processor that can append arbitrary attributes to the first MANIFEST.MF + * that is found in the set of JARs being processed. The attributes are appended in + * the specified order, and duplicates are allowed. + * + * Modified from [ManifestResourceTransformer]. + * @author Chris Rankin + */ +open class ManifestAppenderTransformer : Transformer { + private var manifestContents = ByteArray(0) + private val _attributes = mutableListOf>>() + + @get:Input + open val attributes: List>> get() = _attributes + + override fun canTransformResource(element: FileTreeElement): Boolean { + return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + if (manifestContents.isEmpty()) { + try { + context.inputStream.use { inputStream -> + val outputStream = ByteArrayOutputStream() + inputStream.copyTo(outputStream) + manifestContents = outputStream.toByteArray() + } + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } + } + } + + override fun hasTransformedResource(): Boolean = _attributes.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(MANIFEST_NAME) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + os.write(manifestContents) + + if (_attributes.isNotEmpty()) { + for ((key, value) in _attributes) { + os.write(key.toByteArray()) + os.write(SEPARATOR) + os.write(value.toString().toByteArray()) + os.write(EOL) + } + os.write(EOL) + _attributes.clear() + } + } + + open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { + _attributes.add(Pair(name, value)) + } + + private companion object { + private val logger = LoggerFactory.getLogger(ManifestAppenderTransformer::class.java) + private val EOL = "\r\n".toByteArray() + private val SEPARATOR = ": ".toByteArray() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt new file mode 100644 index 000000000..fb2ced9ce --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -0,0 +1,88 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import java.io.IOException +import java.util.jar.Attributes +import java.util.jar.JarFile +import java.util.jar.Manifest +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.slf4j.LoggerFactory + +/** + * A resource processor that allows the arbitrary addition of attributes to + * the first MANIFEST.MF that is found in the set of JARs being processed, or + * to a newly created manifest for the shaded JAR. + * + * Modified from `org.apache.maven.plugins.shade.resource.ManifestResourceTransformer` + * + * @author Jason van Zyl + * @author John Engelman + */ +open class ManifestResourceTransformer : Transformer { + private var manifestDiscovered = false + private var manifest: Manifest? = null + + @get:Optional + @get:Input + var mainClass: String? = null + + @get:Optional + @get:Input + var manifestEntries: MutableMap? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return JarFile.MANIFEST_NAME.equals(path, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior + // now which is situational at best. Right now there is no context passed in with the processing so we cannot + // tell what artifact is being processed. + if (!manifestDiscovered) { + try { + manifest = Manifest(context.inputStream) + manifestDiscovered = true + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } + } + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + // If we didn't find a manifest, then let's create one. + if (manifest == null) { + manifest = Manifest() + } + + val attributes = manifest!!.mainAttributes + mainClass?.let { + attributes[Attributes.Name.MAIN_CLASS] = it + } + manifestEntries?.forEach { (key, value) -> + attributes[Attributes.Name(key)] = value + } + + val entry = ZipEntry(JarFile.MANIFEST_NAME) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + manifest!!.write(os) + } + + open fun attributes(attributes: Map): ManifestResourceTransformer = apply { + if (manifestEntries == null) { + manifestEntries = LinkedHashMap() + } + manifestEntries!!.putAll(attributes) + } + + private companion object { + private val logger = LoggerFactory.getLogger(ManifestResourceTransformer::class.java) + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt new file mode 100644 index 000000000..1b5b6d0f8 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -0,0 +1,82 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.StringReader +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.jdom2.Document +import org.jdom2.JDOMException +import org.jdom2.input.SAXBuilder +import org.jdom2.input.sax.XMLReaders +import org.jdom2.output.Format +import org.jdom2.output.XMLOutputter +import org.xml.sax.EntityResolver +import org.xml.sax.InputSource + +/** + * Appends multiple occurrences of some XML file. + * + * Modified from `org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java` + * + * @author John Engelman + */ +@CacheableTransformer +open class XmlAppendingTransformer : Transformer { + private var doc: Document? = null + + @get:Input + var ignoreDtd: Boolean = true + + @get:Optional + @get:Input + var resource: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + return resource?.equals(element.relativePath.pathString, ignoreCase = true) == true + } + + override fun transform(context: TransformerContext) { + val r = try { + SAXBuilder(XMLReaders.NONVALIDATING).apply { + expandEntities = false + if (ignoreDtd) { + entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } + } + }.build(context.inputStream) + } catch (e: JDOMException) { + throw RuntimeException("Error processing resource $resource: ${e.message}", e) + } + + if (doc == null) { + doc = r + } else { + val root = r.rootElement + root.attributes.forEach { a -> + val mergedEl = doc!!.rootElement + val mergedAtt = mergedEl.getAttribute(a.name, a.namespace) + if (mergedAtt == null) { + mergedEl.setAttribute(a) + } + } + root.children.forEach { n -> + doc!!.rootElement.addContent(n.clone()) + } + } + } + + override fun hasTransformedResource(): Boolean = doc != null + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(resource) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + XMLOutputter(Format.getPrettyFormat()).output(doc, os) + doc = null + } + + companion object { + const val XSI_NS: String = "http://www.w3.org/2001/XMLSchema-instance" + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy deleted file mode 100644 index be211b1a5..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -/** - * A resource processor that appends content for a resource, separated by a newline. - * - * Modified from org.apache.maven.plugins.shade.resource.AppendingTransformer.java - * - * Modifications - * @author John Engelman - */ -@CacheableTransformer -class AppendingTransformer implements Transformer { - - @Optional - @Input - String resource - - /** - * Defer initialization, see https://github.com/GradleUp/shadow/issues/763 - */ - private ByteArrayOutputStream data - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (resource != null && resource.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - if (data == null) { - data = new ByteArrayOutputStream() - } - - IOUtil.copy(context.inputStream, data) - data.write('\n'.bytes) - - context.inputStream.close() - } - - @Override - boolean hasTransformedResource() { - return data?.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - if (data == null) { - data = new ByteArrayOutputStream() - } - - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - - IOUtil.copy(new ByteArrayInputStream(data.toByteArray()), os) - data.reset() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy deleted file mode 100644 index 656228c8f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import groovy.util.logging.Slf4j -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input - -import static java.nio.charset.StandardCharsets.UTF_8 -import static java.util.jar.JarFile.MANIFEST_NAME - -/** - * A resource processor that can append arbitrary attributes to the first MANIFEST.MF - * that is found in the set of JARs being processed. The attributes are appended in - * the specified order, and duplicates are allowed. - *

- * Modified from {@link ManifestResourceTransformer}. - * @author Chris Rankin - */ -@Slf4j -class ManifestAppenderTransformer implements Transformer { - private static final byte[] EOL = "\r\n".getBytes(UTF_8) - private static final byte[] SEPARATOR = ": ".getBytes(UTF_8) - - private byte[] manifestContents = [] - private final List>> attributes = [] - - @Input - List>> getAttributes() { attributes } - - ManifestAppenderTransformer append(String name, Comparable value) { - attributes.add(new Tuple2>(name, value)) - this - } - - @Override - boolean canTransformResource(FileTreeElement element) { - MANIFEST_NAME.equalsIgnoreCase(element.relativePath.pathString) - } - - @Override - void transform(TransformerContext context) { - if (manifestContents.length == 0) { - manifestContents = IOUtil.toByteArray(context.inputStream) - try { - context.inputStream - } catch (IOException e) { - log.warn("Failed to read MANIFEST.MF", e) - } - } - } - - @Override - boolean hasTransformedResource() { - !attributes.isEmpty() - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - os.write(manifestContents) - - if (!attributes.isEmpty()) { - for (attribute in attributes) { - os.write(attribute.v1.getBytes(UTF_8)) - os.write(SEPARATOR) - os.write(attribute.v2.toString().getBytes(UTF_8)) - os.write(EOL) - } - os.write(EOL) - attributes.clear() - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy deleted file mode 100644 index ccba8b699..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import groovy.util.logging.Slf4j -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import java.util.jar.Attributes -import java.util.jar.Attributes.Name -import java.util.jar.JarFile -import java.util.jar.Manifest - -/** - * A resource processor that allows the arbitrary addition of attributes to - * the first MANIFEST.MF that is found in the set of JARs being processed, or - * to a newly created manifest for the shaded JAR. - *

- * Modified from org.apache.maven.plugins.shade.resource.ManifestResourceTransformer - * @author Jason van Zyl - * @author John Engelman - */ -@Slf4j -class ManifestResourceTransformer implements Transformer { - - // Configuration - @Optional - @Input - String mainClass - - @Optional - @Input - Map manifestEntries - - // Fields - private boolean manifestDiscovered - - private Manifest manifest - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (JarFile.MANIFEST_NAME.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior - // now which is situational at best. Right now there is no context passed in with the processing so we cannot - // tell what artifact is being processed. - if (!manifestDiscovered) { - manifest = new Manifest(context.inputStream) - manifestDiscovered = true - try { - context.inputStream - } catch (IOException e) { - log.warn("Failed to read MANIFEST.MF", e) - } - } - } - - @Override - boolean hasTransformedResource() { - return true - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - // If we didn't find a manifest, then let's create one. - if (manifest == null) { - manifest = new Manifest() - } - - Attributes attributes = manifest.getMainAttributes() - - if (mainClass != null) { - attributes.put(Name.MAIN_CLASS, mainClass) - } - - if (manifestEntries != null) { - for (Map.Entry entry : manifestEntries.entrySet()) { - attributes.put(new Name(entry.getKey()), entry.getValue()) - } - } - - ZipEntry entry = new ZipEntry(JarFile.MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - manifest.write(os) - } - - ManifestResourceTransformer attributes(Map attributes) { - if (manifestEntries == null) { - manifestEntries = [:] - } - manifestEntries.putAll(attributes) - this - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy deleted file mode 100644 index 7d08c02d5..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.jdom2.Attribute -import org.jdom2.Content -import org.jdom2.Document -import org.jdom2.Element -import org.jdom2.JDOMException -import org.jdom2.input.SAXBuilder -import org.jdom2.input.sax.XMLReaders -import org.jdom2.output.Format -import org.jdom2.output.XMLOutputter -import org.xml.sax.EntityResolver -import org.xml.sax.InputSource -import org.xml.sax.SAXException - -/** - * Appends multiple occurrences of some XML file. - *

- * Modified from org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java - * - * @author John Engelman - */ -@CacheableTransformer -class XmlAppendingTransformer implements Transformer { - public static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance" - - @Input - boolean ignoreDtd = true - - @Optional - @Input - String resource - - private Document doc - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (resource != null && resource.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - Document r - try { - SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING) - builder.setExpandEntities(false) - if (ignoreDtd) { - builder.setEntityResolver(new EntityResolver() { - InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { - return new InputSource(new StringReader("")) - } - }) - } - r = builder.build(context.inputStream) - } - catch (JDOMException e) { - throw new RuntimeException("Error processing resource " + resource + ": " + e.getMessage(), e) - } - - if (doc == null) { - doc = r - } else { - Element root = r.getRootElement() - - root.attributes.each { Attribute a -> - - Element mergedEl = doc.getRootElement() - Attribute mergedAtt = mergedEl.getAttribute(a.getName(), a.getNamespace()) - if (mergedAtt == null) { - mergedEl.setAttribute(a) - } - } - - root.children.each { Content n -> - doc.getRootElement().addContent(n.clone()) - } - } - } - - @Override - boolean hasTransformedResource() { - return doc != null - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - new XMLOutputter(Format.getPrettyFormat()).output(doc, os) - - doc = null - } -}