Read text files from source folder OR JAR depending on execution runtime - java

My program displays elements read from a text file. The text files will be stored in a folder found in the package folder containing the .java and .class files so they can be embedded in the jar.
I'm trying to get the application to read the text files properly for both situations
Running from the IDE (Netbeans)
Running from the JAR
Currently I can do point one with no problem, but the code reads using File where as the way I am seeing how to do it with Jars is using InputStream.
The functions which work for the IDE runs
public void loadWidgets() {
ArrayList<String> results = new ArrayList<>();
String dir = new File("").getAbsolutePath() + "/src/Creator/textFiles/widgets/;
System.out.println(dir);
getWidgetFiles(dir, results);
results.stream().forEach((s) -> {
readFile(s); // given a string and it opens the file using a Scanner
});
updateWidgetVariables(); // gui updates
}
public void getWidgetFiles(String dirName, ArrayList<String> filePaths) {
File directory = new File(dirName);
File[] files = directory.listFiles();
for (File file : files) {
if (file.isFile()) {
filePaths.add(file.getName() + "," + file.getAbsolutePath());
} else if (file.isDirectory()) {
getWidgetFiles(file.getAbsolutePath(), filePaths);
}
}
}
So I have a bunch of text files organized by the type of widget it is, so I am running through the /widgets/ directory to find all the text files.
The problem I'm having is how I can go through the directories and files of a Jar? Can they be converted to a file, or read into a string?
I got this code from this question and it can read the files, but I dont know how I can open them using a new Scanner(file); code
CodeSource src = WidgetPanel.class.getProtectionDomain().getCodeSource();
try {
System.out.println("Inside try");
List<String> list = new ArrayList<>();
if (src != null) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream(jar.openStream());
ZipEntry ze = null;
System.out.println(jar.getPath());
System.out.println(zip.getNextEntry());
while ((ze = zip.getNextEntry()) != null) {
String entryName = ze.getName();
System.out.println("Entry name: " + entryName);
if (entryName.startsWith("Creator/textFiles/widgets") && entryName.endsWith(".txt")) {
list.add(entryName);
System.out.println("Added name: " + entryName);
}
}
list.stream().forEach((s) -> {
readFile(s);
});
updateWidgetVariables();
} else {
System.out.println("Src null");
}
}catch (IOException e) {
System.out.println(e.getMessage());
}

Try and obtain a FileSystem associated with your source; the matter then becomes pretty simple. Here is an illustration of how to read, as text, all files from a given FileSystem:
private static final BiPredicate<Path, BasicFileAttributes> FILES
= (path, attrs) -> attrs.isRegularFile();
private static void readAllFilesAsText(final List<Path> paths)
throws IOException
{
for (final Path path: paths)
try (
final Stream<String> stream = Files.lines(path);
) {
stream.forEach(System.out::println);
}
}
private static List<Path> getAllFilesFromFileSystems(final FileSystem fs,
final String pathPrefix)
{
final Path baseDir = fs.getPath(pathPrefix);
try (
final Stream<Path> files = Files.find(baseDir, Integer.MAX_VALUE,
FILES);
) {
return files.collect(Collectors.toList());
}
}
Why the mix of "old style" for loops and "new style" lambdas: it is because lambdas just don't handle checked exceptions... Which means you have to do it. For a workaround, see here.
But the gist of it here is to be able to create a FileSystem out of your sources, and you can do it; yes, you can read jars as such. See here.

I was able to find a solution using the functions I already had.
I first check to see if the text file can be found normally (Run from an IDE). If a file not found exception occurs, it sets ide = false so it tries to read from the jar.
public void loadWidgets() {
boolean ide = true;
String path = "textFiles/widgets/";
ArrayList<String> results = new ArrayList<>();
String dir = new File("").getAbsolutePath() + "/src/Creator/" + path;
try {
getWidgetFiles(dir, results);
results.stream().forEach((s) -> {
readFile(s);
});
updateWidgetVariables();
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
ide = false;
}
if (!ide) {
CodeSource src = WidgetPanel.class.getProtectionDomain().getCodeSource();
try {
List<String> list = new ArrayList<>();
if (src != null) {
URL jar = src.getLocation();
ZipInputStream zip = new ZipInputStream(jar.openStream());
ZipEntry ze = null;
while ((ze = zip.getNextEntry()) != null) {
String entryName = ze.getName();
if (entryName.startsWith("Creator/textFiles/widgets") && entryName.endsWith(".txt")) {
// Wouldnt work until i added the "/" before the entryName
list.add("/"+ entryName);
System.out.println("Added name: " + entryName);
}
}
list.stream().forEach((s) -> {
readJarFile(s);
});
updateWidgetVariables();
} else {
System.out.println("Src null");
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
I then created a new readFile function for reading from a Jar
public void readJarFile(String result) {
String name = result.substring(result.lastIndexOf("/") + 1);
String entireWidget = "";
String line;
ArrayList<String> vars = new ArrayList<>();
int begin, end;
InputStream loc = this.getClass().getResourceAsStream(result);
try (Scanner scan = new Scanner(loc)) {
while (scan.hasNextLine()) {
line = scan.nextLine();
entireWidget += line;
while (line.contains("`%")) {
begin = line.indexOf("`%");
end = line.indexOf("%`") + 2;
vars.add(line.substring(begin, end));
//System.out.println("Variable added: " + line.substring(begin, end));
line = line.substring(end);
}
}
}
System.out.println(name + ": " + entireWidget);
Widget widget = new Widget(name, vars, entireWidget, result);
widgetList.put(name, widget);
}
Most of this answer is thanks to this question

Related

Java Program to return latest file in a folder matching content

I am writing a program in Java which will parse files from a folder and then return the latest file matching a string.
For example, in a folder with 4 files (4th file with latest modified date). I am now writing a program which will return the latest file containing the string and not just the latest file in the folder. In the illustration below, I expect the Java code to return file 3 and not file 4.
Here is the code which I am attempting and am getting null. I am debugging the code now which not sure why it just does not enter while loop.
The below code works.
public class LatestFileMatchingString {
public static void main(String[] args) {
File directory = new File("testDataFolder");
File[] files = directory.listFiles(File::isFile);
long lastModifiedTime = Long.MIN_VALUE;
File chosenFile = null;
if (files != null)
{
for (File file : files)
{
if (file.lastModified() > lastModifiedTime)
{
try {
int count = 0;
FileReader fileIn = new FileReader(file);
BufferedReader reader = new BufferedReader(fileIn);
String line;
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
String newLine = scanner.nextLine();
System.out.println(newLine);
if((newLine.contains("JAVASTRING"))) {
chosenFile = file;
lastModifiedTime = file.lastModified();
System.out.println("This file has string " + file);
}
}
}catch (IOException e){
System.out.println(e);
}
}
}
}
System.out.println("The latest File with matching string is : " + chosenFile);
}
}

How to duplicate a file given a different name in the same directory in windows

I have been trying to duplicate a file but change the name of it in the same windows directory but I got not luck.
I cant just copy the file in the same directory because of the windows rule that two files cannot have the same name in the same directory.
I am not allowed to copy it to another directory then rename it, and then move it back in the same directory.
And I don't see any helpful implementation in the File.class.
Tried something like that but it didnt work:
File file = new File(filePath);
File copiedFile = new File(filePath);
//then rename the copiedFile and then try to copy it
Files.copy(file, copiedFile);
An initial attempt would be using Path as suitable:
Path file = Paths.get(filePath);
String name = file.getFileName().toString();
String copiedName = name.replaceFirst("(\\.[^\\.]*)?$", "-copy$0");
Path copiedFile = file.resolveSibling(copiedName);
try {
Files.copy(file, copiedFile);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
You could create a new file in the same directory and then just copy the contents of the original file to the duplicate
See: Java read from one file and write into another file using methods
For more info
you can also use this snippet from https://www.journaldev.com/861/java-copy-file
private static void copyFileUsingStream(File source, File dest) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(source);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
} finally {
is.close();
os.close();
}
}
#Pierre his code is perfect, however this is what I use so I won't be able to change the extension:
public static void copyWithDifferentName(File sourceFile, String newFileName) {
if (sourceFile == null || newFileName == null || newFileName.isEmpty()) {
return;
}
String extension = "";
if (sourceFile.getName().split("\\.").length > 1) {
extension = sourceFile.getName().split("\\.")[sourceFile.getName().split("\\.").length - 1];
}
String path = sourceFile.getAbsolutePath();
String newPath = path.substring(0, path.length() - sourceFile.getName().length()) + newFileName;
if (!extension.isEmpty()) {
newPath += "." + extension;
}
try (OutputStream out = new FileOutputStream(newPath)) {
Files.copy(sourceFile.toPath(), out);
} catch (IOException e) {
e.printStackTrace();
}
}

List the names of all subdirectories in resources folder of a JAR

I packaged my Spring Boot project into a JAR using maven. I want to read the names of all directories under "static/foo" in the resources folder.
Tried the following using the Apache Commons library:
String fooPath = "static/foo/";
List<String> fooFolders = IOUtils.readLines(this.getClass().getClassLoader().getResourceAsStream(fooPath), Charsets.UTF_8);
// The fooFolders list is empty ...
UPDATE
This solution worked (slightly modified from https://stackoverflow.com/a/48190582/1427624) using JarFile:
String fooPath = "static/foo";
URL url = Thread.currentThread().getContextClassLoader().getResource(fooPath);
// Not running from JAR
if (url.getProtocol().equals("file"))
{
try {
// Get list of subdirectories' folder names
List<String> fooFolders = IOUtils.readLines(this.getClass().getClassLoader().getResourceAsStream(fooPath), Charsets.UTF_8);
// Loop subdirectories
for (String fooFolder : fooFolders) {
// The current subdirectory path
String fooFolderPath = fooPath + "/" + fooFolder;
// Loop all files in this subdirectory, if needed
List<String> fooFiles = IOUtils.readLines(this.getClass().getClassLoader().getResourceAsStream(fooFolderPath), Charsets.UTF_8);
for (String fooFile : fooFiles) {
// The updated path of the file
String fooFilePath = fooFolderPath + "/" + fooFile;
// Read the file's content
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(fooFilePath);
StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, Charsets.UTF_8);
String fileContent = writer.toString();
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
// Running from JAR
else if (url.getProtocol().equals("jar")) {
String dirname = fooPath + "/";
String path = url.getPath();
String jarPath = path.substring(5, path.indexOf("!"));
List<String> fooFolders = new ArrayList<String>();
HashMap<String, List<String>> fooFiles = new HashMap<String, List<String>>();
try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()))) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String jarEntryName = entry.getName();
String updated_dir_name = "BOOT-INF/classes/" + dirname;
// Only get files that are in the directory we require (fooPath)
if (jarEntryName.startsWith(updated_dir_name) && !dirname.equals(updated_dir_name)) {
// Get the resource URL
URL resourceURL = Thread.currentThread().getContextClassLoader().getResource(jarEntryName);
// Files only
if (!jarEntryName.endsWith("/")) {
// Split the foo number and the file name
String[] split = jarEntryName.split("/");
// First level subdirectories inside fooPath
// BOOT-INF/classes/static/foo/1/myfile.html
// We want to read the folder name "1"
String folderName = split[split.length - 2];
// If you want to read this file
// Read the file's content
// InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceURL);
}
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Sounds like this could be solved by https://stackoverflow.com/a/3923685/7610371
The "core" bits from the answer above:
InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream(fooPath);
BufferedReader br = new BufferedReader(new InputStreamReader(resourceStream))
String resource;
while ((resource = br.readLine()) != null) {
// ... resource is the next filename; you can add it to an array
// or use it here
}
I think you should use FileUtils instead.
In particular, the method listFilesAndDirs with the filter DirectoryFileFilter.
You can also simply do the below :
File f = new File("static/foo/");
File[] arr = f.listFiles();
for (File file : arr) {
if (file.isDirectory())
System.out.println(file);
}

File list from classpath directory in running jar

I want to load all resource bundle properties from classpath, so that I can show supported languages. I got a reference from here and tried 1st solution. It works file when I run my code from eclipse. But when I create executable jar file, it could not read files. I don't know why behavior is different while running from command java -jar AppName.jar
My Code Is:
public static List<String> getResourceFiles(String path) throws IOException
{
List<String> filenames = new ArrayList<>();
InputStream in = getResourceAsStream(path);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
System.out.println("br = " + br.readLine());
String resource;
while ((resource = br.readLine()) != null)
{
filenames.add(resource);
}
return filenames;
}
private static InputStream getResourceAsStream(String resource)
{
final InputStream in = getContextClassLoader().getResourceAsStream(resource);
System.out.println("input stream = " + in);
return in == null ? FileUtil.class.getResourceAsStream(resource) : in;
}
private static ClassLoader getContextClassLoader()
{
return Thread.currentThread().getContextClassLoader();
}
Here I noticed that InputStream is null when I run from command, but while running from eclipse InputStream is not null.
How to solve this problem so that I can read resource files when running from command also?
I found solution. Below code worked for me:
public static String[] getResourceListing(Class clazz, String path) throws URISyntaxException, IOException
{
URL dirURL = clazz.getClassLoader().getResource(path);
if (dirURL != null && dirURL.getProtocol().equals("file"))
{
/* A file path: easy enough */
return new File(dirURL.toURI()).list();
}
if (dirURL == null)
{
/*
* In case of a jar file, we can't actually find a directory. Have to assume the
* same jar as clazz.
*/
String me = clazz.getName().replace(".", "/") + ".class";
dirURL = clazz.getClassLoader().getResource(me);
}
if (dirURL.getProtocol().equals("jar"))
{
/* A JAR path */
String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); // strip out only the JAR file
JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
Enumeration<JarEntry> entries = jar.entries(); // gives ALL entries in jar
Set<String> result = new HashSet<String>(); // avoid duplicates in case it is a subdirectory
while (entries.hasMoreElements())
{
String name = entries.nextElement().getName();
if (name.startsWith(path))
{ // filter according to the path
String entry = name.substring(path.length());
int checkSubdir = entry.indexOf("/");
if (checkSubdir >= 0)
{
// if it is a subdirectory, we just return the directory name
entry = entry.substring(0, checkSubdir);
}
result.add(entry);
}
}
return result.toArray(new String[result.size()]);
}
throw new UnsupportedOperationException("Cannot list files for URL " + dirURL);
}

How can I count the number of files in a folder within a JAR?

I've spent a bit of time trying to find a way to count the number of files in a folder within a JAR. I put together several examples of code that served different purposes to make this work. It counts just fine when I run the code through Eclipse but after exporting to a JAR it fails and returns 0. In this case, my folder path I use is just "rules/". I would appreciate any recommendations or samples. Thanks.
public static int countFiles(String folderPath) throws IOException { //Counts the number of files in a specified folder
ClassLoader loader = ToolSet.class.getClassLoader();
InputStream is = loader.getResourceAsStream(folderPath);
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean empty = true;
while ((readChars = is.read(c)) != -1) {
empty = false;
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
}
return (count == 0 && !empty) ? 1 : count;
} finally {
is.close();
}
}
EDIT:
The following doesn't exactly match my original question but thanks to MadProgrammer I was able to reduce my code and eliminate the need to even count the files. The code blow searches every file in my JAR looking for those that end with ".rules", opens the file, searches the file for a string that matches "searchBox.getText()", appends results, and continues on to the next ".rules" file.
StringBuilder results = new StringBuilder();
int count = 0;
JarFile jf = null;
try {
String path = ToolSet.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = URLDecoder.decode(path, "UTF-8");
jf = new JarFile(new File(decodedPath));
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".rules")) {
String name = entry.getName();
InputStream in = ToolSet.class.getResourceAsStream(name);
InputStreamReader isr = new InputStreamReader(in);
BufferedReader bf = new BufferedReader(isr);
String line;
while ((line = bf.readLine()) != null) {
String lowerText = line.toLowerCase();
if(lowerText.indexOf(searchBox.getText().toLowerCase()) > 0) {
results.append(line + "\n");
count++;
}
}
bf.close();
}
}
} catch (IOException ex) {
try {
jf.close();
} catch (Exception e2) {
}
}
if(count>0) {
logBox.setText(results.toString());
} else {
logBox.setText("No matches could be found");
}
A Jar file is essentially a Zip file with a manifest.
Jar/Zip files don't actually have a concept of directories like disks do. They simply have a list of entries that have names. These names may contain some kind path separator and some entries may actually be marked as directories (and tend not to have any bytes associated with them, merely acting as markers)
If you want to find all the resources within a given path, you're going to have to open the Jar file and inspect it's entries yourself, for example...
JarFile jf = null;
try {
String path = "resources";
jf = new JarFile(new File("dist/ResourceFolderCounter.jar"));
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.isDirectory()) {
String name = entry.getName();
name = name.replace(path + "/", "");
if (!name.contains("/")) {
System.out.println(name);
}
}
}
} catch (IOException ex) {
try {
jf.close();
} catch (Exception e) {
}
}
Now, this requires you to know the name of the Jar file you want to use, this may be problematic, as you may wish to list resources from a number of different Jars...
A better solution would be to generate some kind of "resource lookup" file at build time, which contained all the names of the resources that you might need, maybe even keyed to particular names...
This way you could simple use...
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsInputStream("/resources/MasterResourceList.txt")));
String name = null;
while ((name = br.readLine()) != null) {
URL url = getClass().getResource(name);
}
} finally {
try {
br.close();
} catch (Exception exp) {
}
}
For example...
You could even seed the file with the number of resources ;)
this is a simple solution :
InputStream is = loader.getResourceAsStream(folderPath);
//open zip
ZipInputStream zip = new ZipInputStream(is);
//count number of files
while ((zip.getNextEntry()) != null ) {
UnzipCounter++;
}

Categories