blob: 12465c14c42f0f10085cde87a21bb9edaa9a3868 [file] [log] [blame]
// ***************************************************************************************************************************
// * 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.juneau.cp;
import static org.apache.juneau.common.internal.ArgUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
import org.apache.juneau.*;
import org.apache.juneau.internal.*;
/**
* Utility class for finding regular or localized files on the classpath and file system.
*
* <h5 class='section'>Example:</h5>
* <p class='bjava'>
* <jc>// Constructor a file source that looks for files in the "files" working directory, then in the
* // package "foo.bar", then in the package "foo.bar.files", then in the package "files".</jc>
* FileFinder <jv>finder</jv> = FileFinder
* .<jsm>create</jsm>()
* .dir(<js>"files"</js>)
* .cp(foo.bar.MyClass.<jk>class</jk>,<jk>null</jk>,<jk>true</jk>)
* .cp(foo.bar.MyClass.<jk>class</jk>,<js>"files"</js>,<jk>true</jk>)
* .cp(foo.bar.MyClass.<jk>class</jk>,<js>"/files"</js>,<jk>true</jk>)
* .cache(1_000_000l) <jc>// Cache files less than 1MB in size.</jc>
* .ignore(Pattern.<jsm>compile</jsm>(<js>"(?i)(.*\\.(class|properties))|(package.html)"</js>)) <jc>// Ignore certain files.</jc>
* .build();
*
* <jc>// Find a normal file.</jc>
* InputStream <jv>is1</jv> = <jv>finder</jv>.getStream(<js>"text.txt"</js>);
*
* <jc>// Find a localized file called "text_ja_JP.txt".</jc>
* InputStream <jv>is2</jv> = <jv>finder</jv>.getStream(<js>"text.txt"</js>, Locale.<jsf>JAPAN</jsf>);
* </p>
*
* <p>
* If the <c>locale</c> is specified, then we look for resources whose name matches that locale.
* For example, if looking for the resource <js>"MyResource.txt"</js> for the Japanese locale, we will look for
* files in the following order:
* <ol>
* <li><js>"MyResource_ja_JP.txt"</js>
* <li><js>"MyResource_ja.txt"</js>
* <li><js>"MyResource.txt"</js>
* </ol>
*
* <p>
* The default implementation of this interface is {@link BasicFileFinder}.
* The {@link Builder#type(Class)} method is provided for instantiating other instances.
*
* <h5 class='section'>Example:</h5>
* <p class='bjava'>
* <jk>public class</jk> MyFileFinder <jk>extends</jk> BasicFileFinder {
* <jk>public</jk> MyFileFinder(FileFinder.Builder <jv>builder</jv>) {
* <jk>super</jk>(<jv>builder</jv>);
* }
* }
*
* <jc>// Instantiate subclass.</jc>
* FileFinder <jv>myFinder</jv> = FileFinder.<jsm>create</jsm>().type(MyFileFinder.<jk>class</jk>).build();
* </p>
*
* <p>
* Subclasses must provide a public constructor that takes in any of the following arguments:
* <ul>
* <li>{@link Builder} - The builder object.
* <li>Any beans present in the bean store passed into the constructor.
* <li>Any {@link Optional} beans optionally present in bean store passed into the constructor.
* </ul>
*
* <h5 class='section'>See Also:</h5><ul>
* </ul>
*/
public interface FileFinder {
//-----------------------------------------------------------------------------------------------------------------
// Static
//-----------------------------------------------------------------------------------------------------------------
/** Represents no file finder */
public abstract class Void implements FileFinder {}
/**
* Static creator.
*
* @param beanStore The bean store to use for creating beans.
* @return A new builder for this object.
*/
static Builder create(BeanStore beanStore) {
return new Builder(beanStore);
}
/**
* Static creator.
*
* @return A new builder for this object.
*/
static Builder create() {
return new Builder(BeanStore.INSTANCE);
}
//-----------------------------------------------------------------------------------------------------------------
// Builder
//-----------------------------------------------------------------------------------------------------------------
/**
* Builder class.
*/
@FluentSetters
public static class Builder extends BeanBuilder<FileFinder> {
final Set<LocalDir> roots;
long cachingLimit;
Pattern[] include, exclude;
/**
* Constructor.
*
* @param beanStore The bean store to use for creating beans.
*/
protected Builder(BeanStore beanStore) {
super(BasicFileFinder.class, beanStore);
roots = set();
cachingLimit = -1;
include = new Pattern[]{Pattern.compile(".*")};
exclude = new Pattern[0];
}
@Override /* BeanBuilder */
protected FileFinder buildDefault() {
return new BasicFileFinder(this);
}
//-------------------------------------------------------------------------------------------------------------
// Properties
//-------------------------------------------------------------------------------------------------------------
/**
* Adds a class subpackage to the lookup paths.
*
* @param c The class whose package will be added to the lookup paths. Must not be <jk>null</jk>.
* @param path The absolute or relative subpath.
* @param recursive If <jk>true</jk>, also recursively adds all the paths of the parent classes as well.
* @return This object.
*/
@FluentSetter
public Builder cp(Class<?> c, String path, boolean recursive) {
assertArgNotNull("c", c);
while (c != null) {
roots.add(new LocalDir(c, path));
c = recursive ? c.getSuperclass() : null;
}
return this;
}
/**
* Adds a file system directory to the lookup paths.
*
* @param path The path relative to the working directory. Must not be <jk>null</jk>
* @return This object.
*/
@FluentSetter
public Builder dir(String path) {
assertArgNotNull("path", path);
return path(Paths.get(".").resolve(path));
}
/**
* Adds a file system directory to the lookup paths.
*
* @param path The directory path.
* @return This object.
*/
@FluentSetter
public Builder path(Path path) {
roots.add(new LocalDir(path));
return this;
}
/**
* Enables in-memory caching of files for quicker retrieval.
*
* @param cachingLimit The maximum file size in bytes.
* @return This object.
*/
@FluentSetter
public Builder caching(long cachingLimit) {
this.cachingLimit = cachingLimit;
return this;
}
/**
* Specifies the regular expression file name patterns to use to include files being retrieved from the file source.
*
* @param patterns
* The regular expression include patterns.
* <br>The default is <js>".*"</js>.
* @return This object.
*/
@FluentSetter
public Builder include(String...patterns) {
this.include = alist(patterns).stream().map(Pattern::compile).toArray(Pattern[]::new);
return this;
}
/**
* Specifies the regular expression file name pattern to use to exclude files from being retrieved from the file source.
*
* @param patterns
* The regular expression exclude patterns.
* <br>If none are specified, no files will be excluded.
* @return This object.
*/
@FluentSetter
public Builder exclude(String...patterns) {
this.exclude = alist(patterns).stream().map(Pattern::compile).toArray(Pattern[]::new);
return this;
}
// <FluentSetters>
@Override /* GENERATED - org.apache.juneau.BeanBuilder */
public Builder impl(Object value) {
super.impl(value);
return this;
}
@Override /* GENERATED - org.apache.juneau.BeanBuilder */
public Builder type(Class<?> value) {
super.type(value);
return this;
}
// </FluentSetters>
}
//-----------------------------------------------------------------------------------------------------------------
// Instance
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the contents of the resource with the specified name.
*
* @param name The resource name.
* See {@link Class#getResource(String)} for format.
* @param locale
* The locale of the resource to retrieve.
* <br>If <jk>null</jk>, won't look for localized file names.
* @return The resolved resource contents, or <jk>null</jk> if the resource was not found.
* @throws IOException Thrown by underlying stream.
*/
Optional<InputStream> getStream(String name, Locale locale) throws IOException;
/**
* Returns the file with the specified name as a string.
*
* @param name The file name.
* @param locale
* The locale of the resource to retrieve.
* <br>If <jk>null</jk>, won't look for localized file names.
* @return The contents of the file as a string. Assumes UTF-8 encoding.
* @throws IOException If file could not be read.
*/
Optional<String> getString(String name, Locale locale) throws IOException;
}