content: /**
   * ﻿Copyright (C) 2007 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * 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.n52.wps.io;
  
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.ByteArrayInputStream;
  import java.io.File;
  import java.io.FileFilter;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.UUID;
  import java.util.zip.ZipEntry;
  import java.util.zip.ZipInputStream;
  import java.util.zip.ZipOutputStream;
  
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  import javax.xml.transform.TransformerException;
  
  import org.apache.commons.codec.binary.Base64InputStream;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.apache.xpath.XPathAPI;
  import org.w3c.dom.DOMException;
  import org.w3c.dom.Document;
  import org.xml.sax.SAXException;
  
  import static org.apache.commons.io.IOUtils.*;
  
  public class IOUtils {
  	/**
  	 * Reads the given input stream as a string and decodes that base64 string
  	 * into a file with the specified extension
  	 * 
  	 * @param input
  	 *            the stream with the base64 string
  	 * @param extension
  	 *            the extension of the result file (without the '.' at the
  	 *            beginning)
  	 * @return the decoded base64 file written to disk
  	 * @throws IOException
  	 *             if an error occurs while writing the contents to disk
  	 */
  	
  	private static Logger LOGGER = LoggerFactory.getLogger(IOUtils.class);
  	
  	public static File writeBase64ToFile(InputStream input, String extension)
  			throws IOException {
  
          File file = File.createTempFile(
                  "file" + UUID.randomUUID(),
                  "." + extension,
                  new File(System.getProperty("java.io.tmpdir")));
          OutputStream outputStream = null;
          try {
              outputStream = new FileOutputStream(file);
              copyLarge(new Base64InputStream(input), outputStream);
          } finally {
              closeQuietly(outputStream);
          }
  
  		return file;
  	}
  	
  	public static File writeStreamToFile(InputStream inputStream, String extension)
  	throws IOException {
  		File file = File.createTempFile("file" + UUID.randomUUID(), "." + extension);
  		return writeStreamToFile(inputStream, extension, file);
  	}
  
  	public static File writeStreamToFile(InputStream inputStream,
  			String extension, File file) throws IOException {
  		FileOutputStream output = new FileOutputStream(file);
  		
  		byte buf[]=new byte[1024];
  		int len;
  		while((len=inputStream.read(buf))>0){
  			output.write(buf,0,len);
  		}
  		output.close();
  		inputStream.close();
  
  		return file;		
  	}
  
  	public static File writeBase64XMLToFile(InputStream stream, String extension)
  			throws SAXException, IOException, ParserConfigurationException,
  			DOMException, TransformerException {
          
  		// ToDo:  look at StAX to stream XML parsing instead of in memory DOM
  		Document document = DocumentBuilderFactory.newInstance()
  				.newDocumentBuilder().parse(stream);
  		String binaryContent = XPathAPI.selectSingleNode(
  				document.getFirstChild(), "text()").getTextContent();
  
  		InputStream byteStream = null;
          try {
              byteStream = new ByteArrayInputStream(binaryContent.getBytes());
              return writeBase64ToFile(byteStream, extension);
          } finally {
              closeQuietly(byteStream);
          }
  	}
  
  	/**
  	 * Zip the files. Returns a zipped file and delete the specified files
  	 * 
  	 * @param files
  	 *            files to zipped
  	 * @return the zipped file
  	 * @throws IOException
  	 *             if the zipping process fails.
  	 */
  	public static File zip(File... files) throws IOException {
  		File zip = File.createTempFile("zip" + UUID.randomUUID(), ".zip");
  
  		ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zip));
  
  		byte[] buffer = new byte[4096];
  		for (File file : files) {
  			if (!file.exists()) {
  				LOGGER.debug("Could not zip " + file.getAbsolutePath());
  				continue;
  			}
  
  			out.putNextEntry(new ZipEntry(file.getName()));
  			FileInputStream in = new FileInputStream(file);
  
  			int len;
  			while ((len = in.read(buffer)) > 0) {
  				out.write(buffer, 0, len);
  			}
  
  			out.closeEntry();
  			in.close();
  		}
  
  		deleteResources(files);
  
  		out.close();
  
  		return zip;
  	}
  
  	/**
  	 * Unzip the file. Returns the unzipped file with the specified extension
  	 * and deletes the zipped file
  	 * 
  	 * @param file
  	 *            the file to unzip
  	 * @param extension
  	 *            the extension to search in the content files
  	 * @return the file with the specified extension
  	 * @throws IOException
  	 *             if the unzipping process fails
  	 */
  	public static List<File> unzip(File file, String extension) throws IOException {
  		return unzip(file, extension, null);
  	}
  	
  	public static List<File> unzip(File file, String extension, File directory) throws IOException {
  		int bufferLength = 2048;
  		byte buffer[] = new byte[bufferLength];
  		List<File> foundFiles = new ArrayList<File>();
  		ZipInputStream zipInputStream = new ZipInputStream(
  				new BufferedInputStream(new FileInputStream(file)));
  		ZipEntry entry;
  		File tempDir = directory;
  		if (tempDir == null || !directory.isDirectory()) {
  			tempDir = File.createTempFile("unzipped" + UUID.randomUUID(), "", new File(System
  					.getProperty("java.io.tmpdir")));
  			tempDir.delete();
  			tempDir.mkdir();
  		}
  		while ((entry = zipInputStream.getNextEntry()) != null) {
  			int count;
  			File entryFile = new File(tempDir, entry.getName());
  			entryFile.createNewFile();
  			FileOutputStream fos = new FileOutputStream(entryFile);
  			BufferedOutputStream dest = new BufferedOutputStream(fos,
  					bufferLength);
  			while ((count = zipInputStream.read(buffer, 0, bufferLength)) != -1) {
  				dest.write(buffer, 0, count);
  			}
  			dest.flush();
  			dest.close();
  
  			if (entry.getName().endsWith("." + extension)) {
  				foundFiles.add(entryFile);
  				
  			}
  		}
  
  		zipInputStream.close();
  
  		deleteResources(file);
  
  		return foundFiles;
  	}
  	
  	public static List<File> unzipAll(File file) throws IOException {
  		int bufferLength = 2048;
  		byte buffer[] = new byte[bufferLength];
  		List<File> foundFiles = new ArrayList<File>();
  		ZipInputStream zipInputStream = new ZipInputStream(
  				new BufferedInputStream(new FileInputStream(file)));
  		ZipEntry entry;
  		File tempDir = File.createTempFile("unzipped" + UUID.randomUUID(), "", new File(System
  				.getProperty("java.io.tmpdir")));
  		tempDir.delete();
  		tempDir.mkdir();
  		while ((entry = zipInputStream.getNextEntry()) != null) {
  			int count;
  			File entryFile = new File(tempDir, entry.getName());
  			entryFile.createNewFile();
  			FileOutputStream fos = new FileOutputStream(entryFile);
  			BufferedOutputStream dest = new BufferedOutputStream(fos,
  					bufferLength);
  			while ((count = zipInputStream.read(buffer, 0, bufferLength)) != -1) {
  				dest.write(buffer, 0, count);
  			}
  			dest.flush();
  			dest.close();
  
  			foundFiles.add(entryFile);
  			
  		}
  
  		zipInputStream.close();
  
  		deleteResources(file);
  
  		return foundFiles;
  	}
  
  	/**
  	 * Delete the given files and all the files with the same name but different
  	 * extension. If some file is <code>null</code> just doesn't process it and
  	 * continue to the next element of the array
  	 * 
  	 * @param files
  	 *            the files to delete
  	 */
  	public static void deleteResources(File... files) {
  		for (File file : files) {
  			if (file != null) {
  				if (file.getAbsolutePath().startsWith(
  						System.getProperty("java.io.tmpdir"))) {
  					delete(file);
  					File parent = file.getAbsoluteFile().getParentFile();
  					if (parent != null
  							&& !(parent.getAbsolutePath().equals(System
  									.getProperty("java.io.tmpdir")))) {
  						parent.deleteOnExit();
  					}
  				}
  			}
  		}
  	}
  
  	/**
  	 * Delete the given files and all the files with the same name but different
  	 * extension. If some file is <code>null</code> just doesn't process it and
  	 * continue to the next element of the array
  	 * 
  	 * @param files
  	 *            the files to delete
  	 */
  	private static void delete(File... files) {
  		for (File file : files) {
  			if (file != null) {
  				final String baseName = file.getName().substring(0,
  						file.getName().lastIndexOf("."));
  				File[] list = file.getAbsoluteFile().getParentFile().listFiles(
  						new FileFilter() {
  							@Override
  							public boolean accept(File pathname) {
  								return pathname.getName().startsWith(baseName);
  							}
  						});
  				for (File f : list) {
  					f.deleteOnExit();
  				}
  
  				file.deleteOnExit();
  			}
  		}
  	}
  }
  