001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     bstefanescu
011 */
012package org.nuxeo.ecm.automation.core.operations.blob;
013
014import java.io.File;
015import java.io.FileOutputStream;
016import java.io.IOException;
017import java.io.InputStream;
018import java.util.Collection;
019import java.util.HashSet;
020import java.util.zip.ZipOutputStream;
021
022import org.nuxeo.common.utils.StringUtils;
023import org.nuxeo.common.utils.ZipUtils;
024import org.nuxeo.ecm.automation.OperationContext;
025import org.nuxeo.ecm.automation.core.Constants;
026import org.nuxeo.ecm.automation.core.annotations.Context;
027import org.nuxeo.ecm.automation.core.annotations.Operation;
028import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
029import org.nuxeo.ecm.automation.core.annotations.Param;
030import org.nuxeo.ecm.automation.core.util.BlobList;
031import org.nuxeo.ecm.core.api.Blob;
032import org.nuxeo.ecm.core.api.Blobs;
033import org.nuxeo.runtime.api.Framework;
034
035/**
036 * TODO: detect mine?
037 *
038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
039 */
040@Operation(id = CreateZip.ID, category = Constants.CAT_BLOB, label = "Zip", description = "Creates a zip file from the input file(s). If no file name is given, the first file name in the input will be used. Returns the zip file.")
041public class CreateZip {
042
043    public static final String ID = "Blob.CreateZip";
044
045    public static final String ZIP_ENTRY_ENCODING_PROPERTY = "zip.entry.encoding";
046
047    public static enum ZIP_ENTRY_ENCODING_OPTIONS {
048        ascii
049    }
050
051    @Context
052    protected OperationContext ctx;
053
054    @Param(name = "filename", required = false)
055    protected String fileName;
056
057    @OperationMethod
058    public Blob run(Blob blob) throws IOException {
059        if (fileName == null || (fileName = fileName.trim()).length() == 0) {
060            fileName = blob.getFilename();
061        }
062        File file = File.createTempFile("nxops-createzip-", ".tmp");
063        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
064        Framework.trackFile(file, file);
065        try {
066            zip(blob, out);
067        } finally {
068            out.finish();
069            out.close();
070        }
071        return Blobs.createBlob(file, "application/zip", null, fileName);
072    }
073
074    @OperationMethod
075    public Blob run(BlobList blobs) throws IOException {
076        if (fileName == null || (fileName = fileName.trim()).length() == 0) {
077            fileName = blobs.isEmpty() ? null : blobs.get(0).getFilename();
078        }
079        File file = File.createTempFile("nxops-createzip-", ".tmp");
080        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
081        Framework.trackFile(file, file);
082        try {
083            zip(blobs, out);
084        } finally {
085            out.finish();
086            out.close();
087        }
088        return Blobs.createBlob(file, "application/zip", null, fileName);
089    }
090
091    protected String getFileName(Blob blob) {
092        String entry = blob.getFilename();
093        if (entry == null) {
094            entry = "Unknown_" + System.identityHashCode(blob);
095        }
096        return escapeEntryPath(entry);
097    }
098
099    protected void zip(Blob blob, ZipOutputStream out) throws IOException {
100        String entry = getFileName(blob);
101        InputStream in = blob.getStream();
102        try {
103            ZipUtils._zip(entry, in, out);
104        } finally {
105            in.close();
106        }
107    }
108
109    protected void zip(BlobList blobs, ZipOutputStream out) throws IOException {
110        // use a set to avoid zipping entries with same names
111        Collection<String> names = new HashSet<String>();
112        int cnt = 1;
113        for (Blob blob : blobs) {
114            String entry = getFileName(blob);
115            if (!names.add(entry)) {
116                entry = "renamed_" + (cnt++) + "_" + entry;
117            }
118            InputStream in = blob.getStream();
119            try {
120                ZipUtils._zip(entry, in, out);
121            } finally {
122                in.close();
123            }
124        }
125    }
126
127    protected String escapeEntryPath(String path) {
128        String zipEntryEncoding = Framework.getProperty(ZIP_ENTRY_ENCODING_PROPERTY);
129        if (zipEntryEncoding != null && zipEntryEncoding.equals(ZIP_ENTRY_ENCODING_OPTIONS.ascii.toString())) {
130            return StringUtils.toAscii(path, true);
131        }
132        return path;
133    }
134
135}