All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.gitee.starblues.loader.archive.ExplodedArchive Maven / Gradle / Ivy
/*
* Copyright 2012-2021 the original author or authors.
* Copy from spring-boot-loader
*
* 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
*
* https://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 com.gitee.starblues.loader.archive;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.jar.Manifest;
/**
* {@link Archive} implementation backed by an exploded archive directory.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Madhura Bhave
* @since 1.0.0
*/
public class ExplodedArchive implements Archive {
private static final Set SKIPPED_NAMES = new HashSet<>(Arrays.asList(".", ".."));
private final File root;
private final boolean recursive;
private final File manifestFile;
private Manifest manifest;
/**
* Create a new {@link ExplodedArchive} instance.
* @param root the root directory
*/
public ExplodedArchive(File root) {
this(root, true);
}
/**
* Create a new {@link ExplodedArchive} instance.
* @param root the root directory
* @param recursive if recursive searching should be used to locate the manifest.
* Defaults to {@code true}, directories with a large tree might want to set this to
* {@code false}.
*/
public ExplodedArchive(File root, boolean recursive) {
if (!root.exists() || !root.isDirectory()) {
throw new IllegalArgumentException("Invalid source directory " + root);
}
this.root = root;
this.recursive = recursive;
this.manifestFile = getManifestFile(root);
}
private File getManifestFile(File root) {
File metaInf = new File(root, "META-INF");
return new File(metaInf, "MANIFEST.MF");
}
@Override
public URL getUrl() throws MalformedURLException {
return this.root.toURI().toURL();
}
@Override
public Manifest getManifest() throws IOException {
if (this.manifest == null && this.manifestFile.exists()) {
try (FileInputStream inputStream = new FileInputStream(this.manifestFile)) {
this.manifest = new Manifest(inputStream);
}
}
return this.manifest;
}
@Override
public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter) throws IOException {
return new ArchiveIterator(this.root, this.recursive, searchFilter, includeFilter);
}
@Override
public Iterator iterator() {
return new EntryIterator(this.root, this.recursive, null, null);
}
protected Archive getNestedArchive(Entry entry) throws IOException {
File file = ((FileEntry) entry).getFile();
return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive((FileEntry) entry));
}
@Override
public boolean isExploded() {
return true;
}
@Override
public String toString() {
try {
return getUrl().toString();
}
catch (Exception ex) {
return "exploded archive";
}
}
/**
* File based {@link Entry} {@link Iterator}.
*/
private abstract static class AbstractIterator implements Iterator {
private static final Comparator ENTRY_COMPARATOR = Comparator.comparing(File::getAbsolutePath);
private final File root;
private final boolean recursive;
private final EntryFilter searchFilter;
private final EntryFilter includeFilter;
private final Deque> stack = new LinkedList<>();
private FileEntry current;
private final String rootUrl;
AbstractIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) {
this.root = root;
this.rootUrl = this.root.toURI().getPath();
this.recursive = recursive;
this.searchFilter = searchFilter;
this.includeFilter = includeFilter;
this.stack.add(listFiles(root));
this.current = poll();
}
@Override
public boolean hasNext() {
return this.current != null;
}
@Override
public T next() {
FileEntry entry = this.current;
if (entry == null) {
throw new NoSuchElementException();
}
this.current = poll();
return adapt(entry);
}
private FileEntry poll() {
while (!this.stack.isEmpty()) {
Iterator peek = this.stack.peek();
if(peek == null){
continue;
}
while (peek.hasNext()) {
File file = peek.next();
if (SKIPPED_NAMES.contains(file.getName())) {
continue;
}
FileEntry entry = getFileEntry(file);
if (isListable(entry)) {
this.stack.addFirst(listFiles(file));
}
if (this.includeFilter == null || this.includeFilter.matches(entry)) {
return entry;
}
}
this.stack.poll();
}
return null;
}
private FileEntry getFileEntry(File file) {
URI uri = file.toURI();
String name = uri.getPath().substring(this.rootUrl.length());
try {
return new FileEntry(name, file, uri.toURL());
}
catch (MalformedURLException ex) {
throw new IllegalStateException(ex);
}
}
private boolean isListable(FileEntry entry) {
return entry.isDirectory() && (this.recursive || entry.getFile().getParentFile().equals(this.root))
&& (this.searchFilter == null || this.searchFilter.matches(entry))
&& (this.includeFilter == null || !this.includeFilter.matches(entry));
}
private Iterator listFiles(File file) {
File[] files = file.listFiles();
if (files == null) {
return Collections.emptyIterator();
}
Arrays.sort(files, ENTRY_COMPARATOR);
return Arrays.asList(files).iterator();
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
protected abstract T adapt(FileEntry entry);
}
private static class EntryIterator extends AbstractIterator {
EntryIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) {
super(root, recursive, searchFilter, includeFilter);
}
@Override
protected Entry adapt(FileEntry entry) {
return entry;
}
}
private static class ArchiveIterator extends AbstractIterator {
ArchiveIterator(File root, boolean recursive, EntryFilter searchFilter, EntryFilter includeFilter) {
super(root, recursive, searchFilter, includeFilter);
}
@Override
protected Archive adapt(FileEntry entry) {
File file = entry.getFile();
return (file.isDirectory() ? new ExplodedArchive(file) : new SimpleJarFileArchive(entry));
}
}
/**
* {@link Entry} backed by a File.
*/
private static class FileEntry implements Entry {
private final String name;
private final File file;
private final URL url;
FileEntry(String name, File file, URL url) {
this.name = name;
this.file = file;
this.url = url;
}
File getFile() {
return this.file;
}
@Override
public boolean isDirectory() {
return this.file.isDirectory();
}
@Override
public String getName() {
return this.name;
}
URL getUrl() {
return this.url;
}
}
/**
* {@link Archive} implementation backed by a simple JAR file that doesn't itself
* contain nested archives.
*/
private static class SimpleJarFileArchive implements Archive {
private final URL url;
SimpleJarFileArchive(FileEntry file) {
this.url = file.getUrl();
}
@Override
public URL getUrl() throws MalformedURLException {
return this.url;
}
@Override
public Manifest getManifest() throws IOException {
return null;
}
@Override
public Iterator getNestedArchives(EntryFilter searchFilter, EntryFilter includeFilter)
throws IOException {
return Collections.emptyIterator();
}
@Override
@Deprecated
public Iterator iterator() {
return Collections.emptyIterator();
}
@Override
public String toString() {
try {
return getUrl().toString();
}
catch (Exception ex) {
return "jar archive";
}
}
}
}