- /* ====================================================================
- 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 org.apache.poi.openxml4j.opc.internal.marshallers;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.URI;
- import java.util.Objects;
-
- import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
- import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.log4j.Logger;
- import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
- import org.apache.poi.openxml4j.opc.PackageNamespaces;
- import org.apache.poi.openxml4j.opc.PackagePart;
- import org.apache.poi.openxml4j.opc.PackagePartName;
- import org.apache.poi.openxml4j.opc.PackageRelationship;
- import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
- import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
- import org.apache.poi.openxml4j.opc.PackagingURIHelper;
- import org.apache.poi.openxml4j.opc.StreamHelper;
- import org.apache.poi.openxml4j.opc.TargetMode;
- import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
- import org.apache.poi.openxml4j.opc.internal.ZipHelper;
- import org.apache.poi.ooxml.util.DocumentHelper;
- import org.apache.poi.util.IOUtils;
- import org.apache.poi.xssf.usermodel.XSSFRelation;
- import org.w3c.dom.Document;
- import org.w3c.dom.Element;
-
- /**
- * Zip part marshaller. This marshaller is use to save any part in a zip stream.
- */
- public final class ZipPartMarshaller implements PartMarshaller {
- private static final Logger LOG = LogManager.getLogger(ZipPartMarshaller.class);
-
- /**
- * Save the specified part to the given stream.
- *
- * @param part The {@link PackagePart} to save
- * @param os The stream to write the data to
- * @return true if saving was successful or there was nothing to save,
- * false if an error occurred.
- * In case of errors, logging via Log4j 2 is used to provide more information.
- * @throws OpenXML4JException
- * Throws if the stream cannot be written to or an internal exception is thrown.
- */
- @Override
- public boolean marshall(PackagePart part, OutputStream os)
- throws OpenXML4JException {
- if (!(os instanceof ZipArchiveOutputStream)) {
- LOG.atError().log("Unexpected class {}", os.getClass().getName());
- throw new OpenXML4JException("ZipOutputStream expected !");
- // Normally should happen only in development phase, so just throw
- // exception
- }
-
- // check if there is anything to save for some parts. We don't do this for all parts as some code
- // might depend on empty parts being saved, e.g. some unit tests verify this currently.
- if(part.getSize() == 0 && part.getPartName().getName().equals(XSSFRelation.SHARED_STRINGS.getDefaultFileName())) {
- return true;
- }
-
- ZipArchiveOutputStream zos = (ZipArchiveOutputStream) os;
- ZipArchiveEntry partEntry = new ZipArchiveEntry(ZipHelper
- .getZipItemNameFromOPCName(part.getPartName().getURI()
- .getPath()));
- try {
- // Create next zip entry
- zos.putArchiveEntry(partEntry);
-
- // Saving data in the ZIP file
- try (final InputStream ins = part.getInputStream()) {
- IOUtils.copy(ins, zos);
- } finally {
- zos.closeArchiveEntry();
- }
- } catch (IOException ioe) {
- LOG.atError().withThrowable(ioe).log("Cannot write: {}: in ZIP", part.getPartName());
- return false;
- }
-
- // Saving relationship part
- if (part.hasRelationships()) {
- PackagePartName relationshipPartName = PackagingURIHelper
- .getRelationshipPartName(part.getPartName());
-
- marshallRelationshipPart(part.getRelationships(),
- relationshipPartName, zos);
- }
-
- return true;
- }
-
- /**
- * Save relationships into the part.
- *
- * @param rels
- * The relationships collection to marshall.
- * @param relPartName
- * Part name of the relationship part to marshall.
- * @param zos
- * Zip output stream in which to save the XML content of the
- * relationships serialization.
- * @return true if saving was successful,
- * false if an error occurred.
- * In case of errors, logging via Log4j 2 is used to provide more information.
- */
- public static boolean marshallRelationshipPart(
- PackageRelationshipCollection rels, PackagePartName relPartName,
- ZipArchiveOutputStream zos) {
- // Building xml
- Document xmlOutDoc = DocumentHelper.createDocument();
- // make something like <Relationships
- // xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
- Element root = xmlOutDoc.createElementNS(PackageNamespaces.RELATIONSHIPS, PackageRelationship.RELATIONSHIPS_TAG_NAME);
- xmlOutDoc.appendChild(root);
-
- // <Relationship
- // TargetMode="External"
- // Id="rIdx"
- // Target="http://www.custom.com/images/pic1.jpg"
- // Type="http://www.custom.com/external-resource"/>
-
- URI sourcePartURI = PackagingURIHelper
- .getSourcePartUriFromRelationshipPartUri(relPartName.getURI());
-
- for (PackageRelationship rel : rels) {
- // the relationship element
- Element relElem = xmlOutDoc.createElementNS(PackageNamespaces.RELATIONSHIPS, PackageRelationship.RELATIONSHIP_TAG_NAME);
- root.appendChild(relElem);
-
- // the relationship ID
- relElem.setAttribute(PackageRelationship.ID_ATTRIBUTE_NAME, rel.getId());
-
- // the relationship Type
- relElem.setAttribute(PackageRelationship.TYPE_ATTRIBUTE_NAME, rel.getRelationshipType());
-
- // the relationship Target
- String targetValue;
- URI uri = rel.getTargetURI();
- if (Objects.equals(rel.getRelationshipType(), PackageRelationshipTypes.HYPERLINK_PART)) {
- // Save the target as-is - we don't need to validate it,
- targetValue = uri.toString();
- if (rel.getTargetMode() == TargetMode.EXTERNAL) {
- // add TargetMode attribute (as it is external link external)
- relElem.setAttribute(PackageRelationship.TARGET_MODE_ATTRIBUTE_NAME, "External");
- }
- } else if (rel.getTargetMode() == TargetMode.EXTERNAL) {
- // Save the target as-is - we don't need to validate it,
- // alter it etc
- targetValue = uri.toString();
-
- // add TargetMode attribute (as it is external link external)
- relElem.setAttribute(PackageRelationship.TARGET_MODE_ATTRIBUTE_NAME, "External");
- } else {
- URI targetURI = rel.getTargetURI();
- targetValue = PackagingURIHelper.relativizeURI(
- sourcePartURI, targetURI, true).toString();
- }
- relElem.setAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME, targetValue);
- }
-
- xmlOutDoc.normalize();
-
- // String schemaFilename = Configuration.getPathForXmlSchema()+
- // File.separator + "opc-relationships.xsd";
-
- // Save part in zip
- ZipArchiveEntry ctEntry = new ZipArchiveEntry(ZipHelper.getZipURIFromOPCName(
- relPartName.getURI().toASCIIString()).getPath());
- try {
- zos.putArchiveEntry(ctEntry);
- try {
- return StreamHelper.saveXmlInStream(xmlOutDoc, zos);
- } finally {
- zos.closeArchiveEntry();
- }
- } catch (IOException e) {
- LOG.atError().withThrowable(e).log("Cannot create zip entry {}", relPartName);
- return false;
- }
- }
- }
|