001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.utils;
014
015import java.io.*;
016import java.util.*;
017import java.util.zip.*;
018
019/**
020 * Utility class for representing the contents of a zip file as a list of entries whose contents don't resolve until
021 * serialization time.
022 * 
023 * <p>
024 * Generally associated with <code>RestServlets</code> using the <code>responseHandlers</code> annotation so that
025 * REST methods can easily create ZIP file responses by simply returning instances of this class.
026 */
027@SuppressWarnings("serial")
028public class ZipFileList extends LinkedList<ZipFileList.ZipFileEntry> {
029
030   /**
031    * The name of the zip file.
032    */
033   public final String fileName;
034
035   /**
036    * Constructor.
037    * 
038    * @param fileName The file name of the zip file to create.
039    */
040   public ZipFileList(String fileName) {
041      this.fileName = fileName;
042   }
043
044   /**
045    * Add an entry to this list.
046    * 
047    * @param e The zip file entry.
048    * @return This object (for method chaining).
049    */
050   public ZipFileList append(ZipFileEntry e) {
051      add(e);
052      return this;
053   }
054
055   /**
056    * Interface for ZipFileList entries.
057    */
058   public static interface ZipFileEntry {
059      /**
060       * Write this entry to the specified output stream.
061       * 
062       * @param zos The output stream to write to.
063       * @throws IOException
064       */
065      void write(ZipOutputStream zos) throws IOException;
066   }
067
068   /**
069    * ZipFileList entry for File entry types.
070    */
071   public static class FileEntry implements ZipFileEntry {
072
073      /** The root file to base the entry paths on. */
074      protected File root;
075
076      /** The file being zipped. */
077      protected File file;
078
079      /**
080       * Constructor.
081       * 
082       * @param root The root file that represents the base path.
083       * @param file The file to add to the zip file.
084       */
085      public FileEntry(File root, File file) {
086         this.root = root;
087         this.file = file;
088      }
089
090      /**
091       * Constructor.
092       * 
093       * @param file The file to add to the zip file.
094       */
095      public FileEntry(File file) {
096         this.file = file;
097         this.root = (file.isDirectory() ? file : file.getParentFile());
098      }
099
100      @Override /* ZipFileEntry */
101      public void write(ZipOutputStream zos) throws IOException {
102         addFile(zos, file);
103      }
104
105      /**
106       * Subclasses can override this method to customize which files get added to a zip file.
107       * 
108       * @param f The file being added to the zip file.
109       * @return Always returns <jk>true</jk>.
110       */
111      public boolean doAdd(File f) {
112         return true;
113      }
114
115      /**
116       * Adds the specified file to the specified output stream.
117       * 
118       * @param zos The output stream.
119       * @param f The file to add.
120       * @throws IOException
121       */
122      protected void addFile(ZipOutputStream zos, File f) throws IOException {
123         if (doAdd(f)) {
124            if (f.isDirectory()) {
125               File[] fileList = f.listFiles();
126               if (fileList == null)
127                  throw new IOException(f.toString());
128               for (File fc : fileList)
129                  addFile(zos, fc);
130            } else if (f.canRead()) {
131               String path = f.getAbsolutePath().substring(root.getAbsolutePath().length() + 1).replace('\\', '/');
132               ZipEntry e = new ZipEntry(path);
133               e.setSize(f.length());
134               zos.putNextEntry(e);
135               try (FileInputStream fis = new FileInputStream(f)) {
136                  IOPipe.create(fis, zos).run();
137               }
138            }
139         }
140      }
141   }
142}