ArchiveWalker.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you 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.apache.rat.walker;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.io.IOUtils;
import org.apache.rat.api.Document;
import org.apache.rat.api.RatException;
import org.apache.rat.document.ArchiveEntryName;
import org.apache.rat.document.DocumentName;
import org.apache.rat.document.DocumentNameMatcher;
import org.apache.rat.report.RatReport;
import org.apache.rat.utils.DefaultLog;

import static java.lang.String.format;

/**
 * Walks various kinds of archives files.
 */
public class ArchiveWalker extends Walker {

    /**
     * Constructs a walker.
     * @param document the document to process.
     */
    public ArchiveWalker(final Document document) {
        super(document);
    }

    /**
     * Run a report over all files and directories in the archive
     * ignoring any files/directories set to be ignored.
     *
     * @param report the defined RatReport to run on this GZIP walker.
     *
     */
    public void run(final RatReport report) throws RatException {
        for (Document document : getDocuments()) {
            report.report(document);
        }
    }

    /**
     * Creates an input stream from the directory being walked.
     * @return A buffered input stream reading the archive data.
     * @throws IOException on error.
     */
    private InputStream createInputStream() throws IOException {
        return new BufferedInputStream(getDocument().inputStream());
    }

    /**
     * Retrieves the documents from the archive.
     * @return A collection of documents that pass the file filter.
     * @throws RatException on error.
     */
    public Collection<Document> getDocuments() throws RatException {
        List<Document> result = new ArrayList<>();
        try (ArchiveInputStream<? extends ArchiveEntry> input = new ArchiveStreamFactory().createArchiveInputStream(createInputStream())) {
            ArchiveEntry entry;
            while ((entry = input.getNextEntry()) != null) {
                if (!entry.isDirectory() && input.canReadEntryData(entry)) {
                    final DocumentName innerName = DocumentName.builder().setName(entry.getName())
                            .setBaseName(".").build();
                    final DocumentNameMatcher documentNameMatcher = getDocument().getNameMatcher();
                    if (documentNameMatcher.matches(innerName)) {
                        ArchiveEntryName entryName = new ArchiveEntryName(getDocument().getName(), entry.getName());
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        IOUtils.copy(input, baos);
                        result.add(new Document(entryName, documentNameMatcher) {
                            @Override
                            public InputStream inputStream() {
                                return new ByteArrayInputStream(baos.toByteArray());
                            }

                            @Override
                            public boolean isDirectory() {
                                return false;
                            }

                            @Override
                            public SortedSet<Document> listChildren() {
                                return Collections.emptySortedSet();
                            }
                        });
                    }
                }
            }
        } catch (ArchiveException e) {
            DefaultLog.getInstance().warn(format("Unable to process %s: %s", getDocument().getName(), e.getMessage()));
        } catch (IOException e) {
            throw RatException.makeRatException(e);
        }
        return result;
    }
}