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 ByteArrayInputStream bais = null; 118 try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { 119 int bytesRead = 0; 120 final byte bytes[] = new byte[4096]; 121 while((bytesRead = stream.read(bytes)) >= 0) { 122 baos.write(bytes, 0, bytesRead); 123 } 124 baos.flush(); 125 bais = new ByteArrayInputStream(baos.toByteArray()); 126 } catch (final IOException wrapMe) { 127 throw (NoSuchElementException)new NoSuchElementException(wrapMe.getMessage()).initCause(wrapMe); 128 } 129 final Entry<String, InputStream> returnValue = new SimpleImmutableEntry<>(this.currentEntry.getName(), bais); 130 try { 131 this.currentEntry = stream.getNextEntry(); 132 } catch (final IOException ignore) { 133 this.currentEntry = null; 134 } 135 return returnValue; 136 } 137 }; 138 } 139 }; 140 } 141 } 142 143 /** 144 * Does nothing on purpose. 145 * 146 * @exception IOException if a subclass has overridden this method 147 * and an error occurs 148 */ 149 @Override 150 public void close() throws IOException { 151 152 } 153 154 155}