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.NoSuchElementException;
027import java.util.Map.Entry;
028
029import hapi.chart.ChartOuterClass.Chart;
030
031import org.kamranzafar.jtar.TarEntry;
032import org.kamranzafar.jtar.TarInputStream;
033
034/**
035 * A {@link StreamOrientedChartLoader
036 * StreamOrientedChartLoader<TarInputStream>} that creates
037 * {@link Chart} instances from {@link TarInputStream} instances.
038 *
039 * @author <a href="https://about.me/lairdnelson"
040 * target="_parent">Laird Nelson</a>
041 *
042 * @see #toNamedInputStreamEntries(TarInputStream)
043 *
044 * @see StreamOrientedChartLoader
045 */
046public class TapeArchiveChartLoader extends StreamOrientedChartLoader<TarInputStream> {
047
048
049  /*
050   * Constructors.
051   */
052
053
054  /**
055   * Creates a new {@link TapeArchiveChartLoader}.
056   */
057  public TapeArchiveChartLoader() {
058    super();
059  }
060
061
062  /*
063   * Instance methods.
064   */
065
066  
067  /**
068   * Converts the supplied {@link TarInputStream} 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 TarInputStream} 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 TarInputStream}
087   */
088  @Override
089  protected Iterable<? extends Entry<? extends String, ? extends InputStream>> toNamedInputStreamEntries(final TarInputStream 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 TarEntry 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}