001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2017 MicroBean. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.helm.chart; 018 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.IOException; 022import java.io.InputStream; 023 024import java.util.AbstractMap.SimpleImmutableEntry; 025import java.util.Iterator; 026import java.util.Map.Entry; 027import java.util.NoSuchElementException; 028 029import java.util.zip.ZipEntry; 030import java.util.zip.ZipInputStream; 031 032import hapi.chart.ChartOuterClass.Chart; // for javadoc only 033 034/** 035 * A {@link StreamOrientedChartLoader 036 * StreamOrientedChartLoader<ZipInputStream>} that creates 037 * {@link Chart} instances from {@link ZipInputStream} instances. 038 * 039 * @author <a href="https://about.me/lairdnelson" 040 * target="_parent">Laird Nelson</a> 041 * 042 * @see #toNamedInputStreamEntries(ZipInputStream) 043 * 044 * @see StreamOrientedChartLoader 045 */ 046public class ZipInputStreamChartLoader extends StreamOrientedChartLoader<ZipInputStream> { 047 048 049 /* 050 * Constructors. 051 */ 052 053 054 /** 055 * Creates a new {@link ZipInputStreamChartLoader}. 056 */ 057 public ZipInputStreamChartLoader() { 058 super(); 059 } 060 061 062 /* 063 * Instance methods. 064 */ 065 066 067 /** 068 * Converts the supplied {@link ZipInputStream} into an {@link 069 * Iterable} of {@link Entry} instances, each of which consists of 070 * an {@link InputStream} representing an entry within the archive 071 * together with its name. 072 * 073 * <p>This method never returns {@code null}.</p> 074 * 075 * <p>Overrides of this method are not permitted to return {@code 076 * null}. 077 * 078 * @param stream the {@link ZipInputStream} to read; must be 079 * non-{@code null} or an effectively empty {@link Iterable} will be 080 * returned 081 * 082 * @return a non-{@code null} {@link Iterable} of {@link Entry} 083 * instances representing named {@link InputStream}s 084 * 085 * @exception IOException if there is a problem reading from the 086 * supplied {@link ZipInputStream} 087 */ 088 @Override 089 protected Iterable<? extends Entry<? extends String, ? extends InputStream>> toNamedInputStreamEntries(final ZipInputStream stream) throws IOException { 090 if (stream == null) { 091 return new EmptyIterable(); 092 } else { 093 return new Iterable<Entry<String, InputStream>>() { 094 @Override 095 public Iterator<Entry<String, InputStream>> iterator() { 096 return new Iterator<Entry<String, InputStream>>() { 097 private ZipEntry currentEntry; 098 099 { 100 try { 101 this.currentEntry = stream.getNextEntry(); 102 } catch (final IOException ignore) { 103 this.currentEntry = null; 104 } 105 } 106 107 @Override 108 public boolean hasNext() { 109 return this.currentEntry != null; 110 } 111 112 @Override 113 public Entry<String, InputStream> next() { 114 if (this.currentEntry == null) { 115 throw new NoSuchElementException(); 116 } 117 final Entry<String, InputStream> returnValue; 118 if (this.currentEntry.isDirectory()) { 119 returnValue = new SimpleImmutableEntry<>(this.currentEntry.getName(), null); 120 } else { 121 ByteArrayInputStream bais = null; 122 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 123 int bytesRead = 0; 124 final byte bytes[] = new byte[4096]; 125 while((bytesRead = stream.read(bytes)) >= 0) { 126 baos.write(bytes, 0, bytesRead); 127 } 128 baos.flush(); 129 bais = new ByteArrayInputStream(baos.toByteArray()); 130 } catch (final IOException wrapMe) { 131 throw (NoSuchElementException)new NoSuchElementException(wrapMe.getMessage()).initCause(wrapMe); 132 } 133 returnValue = new SimpleImmutableEntry<>(this.currentEntry.getName(), bais); 134 } 135 try { 136 this.currentEntry = stream.getNextEntry(); 137 } catch (final IOException ignore) { 138 this.currentEntry = null; 139 } 140 return returnValue; 141 } 142 }; 143 } 144 }; 145 } 146 } 147 148 /** 149 * Does nothing on purpose. 150 * 151 * @exception IOException if a subclass has overridden this method 152 * and an error occurs 153 */ 154 @Override 155 public void close() throws IOException { 156 157 } 158 159 160}