001// Generated by delombok at Sat Feb 25 17:58:34 UTC 2023
002package io.cui.tools.io;
003
004import static io.cui.tools.base.Preconditions.checkState;
005import static io.cui.tools.string.MoreStrings.isEmpty;
006import static io.cui.tools.string.MoreStrings.requireNotEmpty;
007import static java.util.Objects.requireNonNull;
008import java.io.IOException;
009import java.io.InputStream;
010import java.net.URL;
011import io.cui.tools.lang.SecuritySupport;
012import io.cui.tools.logging.CuiLogger;
013
014/**
015 * Variant of {@link FileLoader} that loads files from the classpath.
016 *
017 * @author Oliver Wolff
018 */
019public class ClassPathLoader implements FileLoader {
020    private static final long serialVersionUID = 9140071059594577808L;
021    private static final CuiLogger log = new CuiLogger(ClassPathLoader.class);
022    private final String normalizedPathName;
023    private final String givenPathName;
024    private final StructuredFilename fileName;
025    private URL url;
026
027    /**
028     * @param pathName must not be null nor empty, may start with the prefix
029     *            {@link FileTypePrefix#CLASSPATH} but not with
030     *            {@link FileTypePrefix#FILE} and contain at least one character despite the prefix.
031     *            On all other cases a {@link IllegalArgumentException} will be
032     *            thrown.
033     */
034    public ClassPathLoader(final String pathName) {
035        requireNonNull(pathName);
036        givenPathName = pathName;
037        normalizedPathName = checkClasspathName(pathName);
038        fileName = new StructuredFilename(FilenameUtils.getName(normalizedPathName));
039    }
040
041    /**
042     * Checks and modifies a given pathName
043     *
044     * @param pathName must not be null nor empty, may start with the prefix
045     *            {@link FileTypePrefix#CLASSPATH} but not with
046     *            {@link FileTypePrefix#FILE} and contain at least one character despite
047     *            the prefix. On all other cases a {@link IllegalArgumentException} will be
048     *            thrown.
049     * @return the normalized pathname without prefix but with a leading '/'
050     */
051    static String checkClasspathName(final String pathName) {
052        requireNotEmpty(pathName);
053        if (FileTypePrefix.FILE.is(pathName)) {
054            throw new IllegalArgumentException("Invalid path name, must start not start with " + FileTypePrefix.FILE + " but was: " + pathName);
055        }
056        var newPathName = pathName;
057        if (FileTypePrefix.CLASSPATH.is(pathName)) {
058            newPathName = FileTypePrefix.CLASSPATH.removePrefix(pathName);
059        }
060        if (isEmpty(newPathName)) {
061            throw new IllegalArgumentException("Filename " + pathName + " is invalid");
062        }
063        if (newPathName.indexOf('/') != 0) {
064            newPathName = '/' + newPathName;
065        }
066        return newPathName;
067    }
068
069    @Override
070    public boolean isReadable() {
071        return null != getURL();
072    }
073
074    @Override
075    public InputStream inputStream() {
076        checkState(isReadable(), "Resource \'{}\' is not readable", givenPathName);
077        try {
078            return getURL().openStream();
079        } catch (IOException e) {
080            throw new IllegalStateException("Unable to load classpath file for " + givenPathName, e);
081        }
082    }
083
084    @Override
085    public boolean isFilesystemLoader() {
086        return false;
087    }
088
089    @Override
090    public URL getURL() {
091        if (null == url) {
092            url = resolveUrl(normalizedPathName);
093        }
094        return url;
095    }
096
097    private static URL resolveUrl(String path) {
098        log.debug("Resolving URL for \'{}\'", path);
099        var url = ClassPathLoader.class.getResource(path);
100        if (null != url) {
101            log.debug("Resolved \'{}\' from ClassPathLoader.class", path);
102            return url;
103        }
104        var loader = SecuritySupport.getContextClassLoader();
105        if (loader.isPresent()) {
106            url = loader.get().getResource(path);
107            if (null != url) {
108                log.debug("Resolved \'{}\' from ContextClassLoader", path);
109                return url;
110            }
111        }
112        log.warn("Unable to resolve \'{}\' from classpath", path);
113        return null;
114    }
115
116    @java.lang.Override
117    @java.lang.SuppressWarnings("all")
118    @lombok.Generated
119    public boolean equals(final java.lang.Object o) {
120        if (o == this) return true;
121        if (!(o instanceof ClassPathLoader)) return false;
122        final ClassPathLoader other = (ClassPathLoader) o;
123        if (!other.canEqual((java.lang.Object) this)) return false;
124        final java.lang.Object this$normalizedPathName = this.normalizedPathName;
125        final java.lang.Object other$normalizedPathName = other.normalizedPathName;
126        if (this$normalizedPathName == null ? other$normalizedPathName != null : !this$normalizedPathName.equals(other$normalizedPathName)) return false;
127        return true;
128    }
129
130    @java.lang.SuppressWarnings("all")
131    @lombok.Generated
132    protected boolean canEqual(final java.lang.Object other) {
133        return other instanceof ClassPathLoader;
134    }
135
136    @java.lang.Override
137    @java.lang.SuppressWarnings("all")
138    @lombok.Generated
139    public int hashCode() {
140        final int PRIME = 59;
141        int result = 1;
142        final java.lang.Object $normalizedPathName = this.normalizedPathName;
143        result = result * PRIME + ($normalizedPathName == null ? 43 : $normalizedPathName.hashCode());
144        return result;
145    }
146
147    @java.lang.Override
148    @java.lang.SuppressWarnings("all")
149    @lombok.Generated
150    public java.lang.String toString() {
151        return "ClassPathLoader(normalizedPathName=" + this.normalizedPathName + ", givenPathName=" + this.givenPathName + ", fileName=" + this.getFileName() + ", url=" + this.getURL() + ")";
152    }
153
154    @java.lang.SuppressWarnings("all")
155    @lombok.Generated
156    public StructuredFilename getFileName() {
157        return this.fileName;
158    }
159}