Read methods from a text file and execute them in the program - java

I have a text file and that file lists all the operations that can be performed on a Pump Class.
example of the content of text file
Start PayCredit Reject Start PayCredit Reject TurnOff
....
.... so on.
These are the methods of the Pump class(Start(), Reject() etc)
I need to write a code where I can Read these method from the file one by one and execute them.
public static void main(String[] args) throws IOException
{
Pump gp= new Pump();
File file=new File("C:\\Users\\Desktop\\checker\\check.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line=null;
while((line=br.readLine())!=null)
{
String words[]=line.split(" ");
for(int i=0;i<words.length;i++)
{
String temp=words[i]+"()";
gp.temp; //compilation error
}
}
}
Could you tell me how can I achieve this functionality.

If you're not so familiar with reflection, maybe try using org.springframework.util.ReflectionUtils from the Spring Framework project?
The code would go something like this:
Pump gp = new Pump();
....
String temp = // from text file
....
Method m = ReflectionUtils.findMethod(Pump.class, temp);
Object result = ReflectionUtils.invokeMethod(m, gp);

You would need to use reflection to invoke the methods at runtime. Here is a simple example that assumes that all methods do not take any parameters.
Class<? extends Pump> pumpClass = gp.getClass();
String methodName = words[i];
Method toInvoke = pumpClass.getMethod(methodName);
if (null != toInvoke) {
toInvoke.invoke(gp);
}

First of all be aware that Java is not interpreted at runtime. So you can't do it this way.
If you already have the methods such as Start PayCredit Reject TurnOff and so on you can do it in the following way:
for(int i=0;i<words.length;i++)
{
String temp=words[i];
if (temp.equals("Start") gp.Start();
else if (temp.equals("PayCredit") gp.PayCredit();
...
}

use a switch case
for(int i=0;i<words.length;i++) {
String temp=words[i];
switch(temp) {
case "Start":
gp.start();
break;
case "PayCredit":
gp.PayCredit();
break;
}
}

You can use reflection to do this, e.g.
String line=null;
Method method = null;
while((line=br.readLine())!=null)
{
String words[]=line.split(" ");
for(int i=0;i<words.length;i++)
{
String temp=words[i];
method = getClass().getMethod(temp);
method.invoke(this);
}
}
That's assuming you want to call the method on this, of course, and that it's an instance method. Look at Class.getMethod and related methods, along with Method itself, for more details. You may want getDeclaredMethod instead, and you may need to make it accessible.
I would see if you can think of a way of avoiding this if possible though - reflection tends to get messy quickly. It's worth taking a step back and considering if this is the best design. If you give us more details of the bigger picture, we may be able to suggest alternatives.

Related

how do you return a boolean for if scanner finds a specified string within a file?

I'm not very familiar with File & Scanner objects so please bear with me:
I'm attempting to have a scanner look through a file and see if a specific string exists, then return true/false - I thought there would be a method for this but either I'm reading the docs wrong or it doesn't exist.
What I'm able to come up with is the following but I'm sure there's a simpler way.
public boolean findString(File f, String s) throws FileNotFoundException {
Scanner scan = new Scanner(f);
if(scan.findWithinHorizon(s, 0) != null) {
return true;
} else {
return false;
}
}
Well, there are many ways to check whether a certain file contains a certain string, but I can't think of a single method which opens the file, scans for the given pattern and then returns a boolean indicating whether the pattern has been found within the file.
I think that use-case would be to small, as in many cases, you want to do more with the contents than only searching whether it contains a specific pattern. Often you want to process the file contents.
Regarding your code, it is already fairly short. However, I would change two things here. First, as scan.findWithinHorizon(s, 0) != null already is a boolean expression, you could immediately return it, instead of using if-else. Second, you should close the file you opened. You can use the try-with-resources construct for this.
Here is the updated code:
try (Scanner scan = new Scanner(f)) {
return scan.findWithinHorizon(s, 0) != null;
}
Note that this code finds a pattern. If you want to find a literal string, then use scan.findWithinHorizon(Pattern.compile(s, Pattern.LITERAL), 0).
More on finding a pattern in a file: this Stack Overflow post
More on try-with-resources: Oracle Java documentation, Baeldung
I would use a while-loop and simply use indexOf to compare the currentLine to your string.
public boolean findString(File f, String s) throws FileNotFoundException {
Scanner scan = new Scanner(f);
String currentLine;
while((currentLine = scanner.readLine()) != null) {
if(currentLine.indexOf(s)) {
return true;
}
}
return false;
}
An advantage of doing it this way is that you can also have an integer which you increase with every run of the loop to get the line in which the string is included (if you want/need to).

Writing output to text file in a class in java? [duplicate]

This question already has answers here:
How do I create a file and write to it?
(35 answers)
Closed 8 years ago.
I'm having an issue of writing output to a text file. I'm not too sure on how to go about it. I've tried to write output by writing something along the lines of- Writer is an object of the printWriter class:
This is trimmed down substantially but it boils down to.
Writer.println(sortRI());
I can't write that statement as the method itself- and all methods in this class I'm trying to write to an output file for are void.
So my question is how do I write output to a text file from my methods which are void- they print out their results via a few print statements. Is there a way I can instantiate a writer and use the writer to print the methods. An example of one of the methods is shown below:
ZipCodeLocation[] data2 = new ZipCodeLocation[];
public class Processor
{
...
public void readData(){
...
}
public void findSpringfield()
{
System.out.println("------------------------------------------------------------------------");
System.out.println("SPRINGFIELD ZIP CODES");
for(int i=0; i < data2.length ; i++)
{
if(data2[i].getpostalCity().replaceAll("\"","").contains("SPRINGFIELD"))
{
System.out.println(data2[i].getzipCode().replaceAll("\"","")); //Prints out zipCodes that have Springfield in them
}
}
}
Instead of printing the message to System.out in the console, how could I have it write the output of the method to a text file?
You have asked a very good question, because it describes an instance of a design consideration common to all software.
The topic here is separation of business logic from interfaces. Even more generally, it's a discussion of well-defined responsibilities in an object-oriented program.
Ideally your business logic would return a data structure, which would allow the caller to do anything desired with the result, like write it to a file or display it in a web page.
public List<String> findSpringfield()
{
List<String> results = new ArrayList<String>();
for(int i=0; i < data2.length ; i++) {
if(data2[i].getpostalCity().contains("SPRINGFIELD")) {
results.add(data2[i].getzipCode().replaceAll("\"","")); //Collects zipCodes that have Springfield in them
}
}
return results;
}
If that's not an option, at the absolute minimum you would want to modify your methods to accept a PrintStream parameter.
public void findSpringfield(PrintStream out)
{
out.println("------------------------------------------------------------------------");
out.println("SPRINGFIELD ZIP CODES");
for(int i=0; i < data2.length ; i++) {
if(data2[i].getpostalCity().contains("SPRINGFIELD")) {
out.println(data2[i].getzipCode().replaceAll("\"","")); //Prints out zipCodes that have Springfield in them
}
}
}
That way you could call it like
findSpringfield(System.out);
Or you could pass it a different PrintStream instance than System.out, one that points to your own file.
Finally, if neither of the above options will work for you, I'm going to tell you how to do something which is very bad practice. You can redirect System.out to a file of your choosing. As I said, this is a terrible idea; use it only as a last resort in a very limited scope.
System.out = new PrintStream("somePath/someFile.txt");
Also, as a design improvement, consider generalizing your findSpringfield method to accept a String postalCity parameter so you can reuse it to find zip codes for other cities.
public List<String> zipsByCity(String postalCity)
{
// use postalCity to filter results, rather than hardcoded "SPRINGFIELD" string
}
Try sending in the Writer as an argument to the method and instead of printing to standard out inside the method, print to the writer:
public void findSpringfield(FileWriter writer)
{
writer.println("--------------------------------------------------- ---------------------");
writer.println("SPRINGFIELD ZIP CODES");
for(int i=0; i < data2.length ; i++)
{
if(data2[i].getpostalCity().replaceAll("\"","").contains("SPRINGFIELD"))
{
writer.println(data2[i].getzipCode().replaceAll("\"","")); //Prints out zipCodes that have Springfield in them
}
}
File file = new File("out.txt");
BufferedWriter output = new BufferedWriter(new FileWriter(file));
for(int i=0; i < data2.length ; i++){
if(data2[i].getpostalCity().replaceAll("\"","").contains("SPRINGFIELD")){
output.write(data2[i].getzipCode().replaceAll("\"",""));
}
}
output.close();
this should work but it will require a try-catch block or a throws declaration
The way it looks right now every single one of your methods has to implement writing to a file or get file writer as an argument. Both of those will work, but will be very ineficcient.
From what I understood from your question you will be having more of those void methods named findXYZ.
Why not write method void findTown(String townName)?
I might be wrong or get wrong impression of what you're trying to achieve but if I'm right then you're going in a wrong direction.

Creating a simple String parser, Interactive Shell-like

I want to make a simple interative shell based on the console where I can write commands like login, help, et cetera.
I first thought of using Enums, but then I didn't know how to implement them neatly without a load of if-else statements, so I decided to go with an array-approach and came up with this:
public class Parser {
private static String[] opts = new String[] {"opt0", "opt1", "opt2", "opt3" ... }
public void parse(String text) {
for(int i = 0; i < opts.length; i++) {
if(text.matches(opts[i]) {
switch(i) {
case 0:
// Do something
case 1:
// Do something-something
case 2:
// Do something else
}
return;
}
}
}
}
But I ended up seeing that this was probably the most rudimentary way of doing something like this, and that there would be problems if I wanted to change the order of the options. How could I make a simpler parser? This way it would work, but it would also have said problems. The use of the program is purely educational, not intended for any serious thing.
A simple approach is to have a HashMap with the key equal to the command text and the value is an instance of class that handle this command. Assuming that the command handler class does not take arguments (but you can easily extend this) you can just use a Runnable instance.
Example code:
Runnable helpHandler = new Runnable() {
public void run(){
// handle the command
}
}
// Define all your command handlers
HashMap<String, Runnable> commandsMap = new HashMap<>(); // Java 7 syntax
commandsMap.put("help",helpHandler);
// Add all your command handlers instances
String cmd; // read the user input
Runnable handler;
if((handler = commandsMap.get(cmd)) != null) {
handler.run();
}
You can easily extend this approach to accept argument by implementing your own interface and subclass it. It is good to use variable arguments if you know the data type e.g. void execute(String ... args)
One solution that comes to mind is actually using Design patterns. You could use the input from the user, as the discriminator for a Factory class.
This factory class will generate an object, with an "execute" method, based on the input. This is called a Command object.
Then you can simply call the method of the object returned from the factory.
No need for a switch statement. If the object is null, then you know the user entered an invalid option, and it abstracts the decision logic away from your input parser.
Hopefully this will help :)

Getting an InputStream to read more than once, regardless of markSupported()

I need to be able to re-use a java.io.InputStream multiple times, and I figured the following code would work, but it only works the first time.
Code
public class Clazz
{
private java.io.InputStream dbInputStream, firstDBInputStream;
private ArrayTable db;
public Clazz(java.io.InputStream defDB)
{
this.firstDBInputStream = defDB;
this.dbInputStream = defDB;
if (db == null)
throw new java.io.FileNotFoundException("Could not find the database at " + db);
if (dbInputStream.markSupported())
dbInputStream.mark(Integer.MAX_VALUE);
loadDatabaseToArrayTable();
}
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
this.dbInputStream = firstDBInputStream;
if (dbInputStream.markSupported())
dbInputStream.reset();
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
String CSV = "";
for (int i = 0; fileScanner.hasNextLine(); i++)
CSV += fileScanner.nextLine() + "\n";
db = ArrayTable.createArrayTableFromCSV(CSV);
}
public void reloadDatabase()//A method called by the UI
{
try
{
loadDatabaseToArrayTable();
}
catch (Throwable t)
{
//Alert the user that an error has occurred
}
}
}
Note that ArrayTable is a class of mine, which uses arrays to give an interface for working with tables.
Question
In this program, the database is shown directly to the user immediately after the reloadDatabase() method is called, and so any solution involving saving the initial read to an object in memory is useless, as that will NOT refresh the data (think of it like a browser; when you press "Refresh", you want it to fetch the information again, not just display the information it fetched the first time). How can I read a java.io.InputStream more than once?
You can't necessarily read an InputStream more than once. Some implementations support it, some don't. What you are doing is checking the markSupported method, which is indeed an indicator if you can read the same stream twice, but then you are ignoring the result. You have to call that method to see if you can read the stream twice, and if you can't, make other arrangements.
Edit (in response to comment): When I wrote my answer, my "other arrangements" was to get a fresh InputStream. However, when I read in your comments to your question about what you want to do, I'm not sure it is possible. For the basics of the operation, you probably want RandomAccessFile (at least that would be my first guess, and if it worked, that would be the easiest) - however you will have file access issues. You have an application actively writing to a file, and another reading that file, you will have problems - exactly which problems will depend on the OS, so whatever solution would require more testing. I suggest a separate question on SO that hits on that point, and someone who has tried that out can perhaps give you more insight.
you never mark the stream to be reset
public Clazz(java.io.InputStream defDB)
{
firstDBInputStream = defDB.markSupported()?defDB:new BufferedInputStream(defDB);
//BufferedInputStream supports marking
firstDBInputStream.mark(500000);//avoid IOException on first reset
}
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
this.dbInputStream = firstDBInputStream;
dbInputStream.reset();
dbInputStream.mark(500000);//or however long the data is
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
StringBuilder CSV = "";//StringBuilder is more efficient in a loop
while(fileScanner.hasNextLine())
CSV.append(fileScanner.nextLine()).append("\n");
db = ArrayTable.createArrayTableFromCSV(CSV.toString());
}
however you could instead keep a copy of the original ArrayTable and copy that when you need to (or even the created string to rebuild it)
this code creates the string and caches it so you can safely discard the inputstreams and just use readCSV to build the ArrayTable
private String readCSV=null;
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
if(readCSV==null){
this.dbInputStream = firstDBInputStream;
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
StringBuilder CSV = "";//StringBuilder is more efficient in a loop
while(fileScanner.hasNextLine())
CSV.append(fileScanner.nextLine()).append("\n");
readCSV=CSV.toString();
fileScanner.close();
}
db = ArrayTable.createArrayTableFromCSV(readCSV);
}
however if you want new information you'll need to create a new stream to read from again

Having trouble opening a file in Java

I am trying to open this file in java and i want to know what i am doing wrong. The in file lies in the same directory as my Java file, but i tried to open this with both netbeans and eclipse and it gave a file not found exception. Can someone help me open this file and read from it. I am really new to java files. Here is the code
import java.util.*;
import java.io.*;
public class Practice
{
public static void main(String[] args)throws IOException
{
FileReader fin = new FileReader("anagrams.in");
BufferedReader br = new BufferedReader(fin);
System.out.println(fin);
String string = "Madam Curie";
String test = "Radium came";
string = string.toLowerCase();
test = test.toLowerCase();
string = string.replaceAll("[^a-zA-Z0-9]+", "");
test = test.replaceAll("[^a-zA-Z0-9]+", "");
char[] array = string.toCharArray();
char[] array2 = test.toCharArray();
boolean flag = false;
HashMap hm = new HashMap();
for(int i = 0; i < array.length; i++)
{
hm.put(array[i], array[i]);
}
for(int i = 0; i < array2.length; i++)
{
if(hm.get(array2[i]) == null || test.length() != string.length())
{
flag = false;
i = array2.length;
}
else
{
flag = true;
}
}
System.out.println(flag);
}
}
A few tips:
Abide to proper code indentation
If you're using an IDE like Eclipse, it can automatically correct indentation for you
Develop debugging instinct
Try to get what the current working directory is, and list all the files in it
Refactor repetitive code
Writing paired statements like you did should immediately raise red flags
Effective Java 2nd Edition
Item 23: Don't use raw types in new code
Item 52: Refer to objects by their interfaces
Item 46: Prefer for-each loops to traditional for loops
Use sensible variable names
With regards to 2, try something like this:
public static void listDir() {
File current = new File(".");
System.out.println(current.getAbsolutePath());
for (String filename : current.list()) {
System.out.println(filename);
}
}
Then in your main, simply call listDir before everything else, and see if you're running the app from the right directory, and if there's a "anagrams.in" in the directory. Note that some platforms are case-sensitive.
With regards to 3 and 4, consider having a helper method like this:
static Set<Character> usedCharactersIn(String s) {
Set<Character> set = new HashSet<Character>();
for (char ch : s.toLowerCase().toCharArray()) {
set.add(ch);
}
return set;
}
Note how Set<E> is used instead of Map<K,V>. Looking at the rest of the code, you didn't seem to actually need a mapping, but rather a set of some sort (but more on that later).
You can then have something like this in main, which makes the logic very readable:
String s1 = ...;
String s2 = ...;
boolean isNotQuiteAnagram = (s1.length() == s2.length()) &&
usedCharactersIn(s1).containsAll(usedCharactersIn(s2));
Note how variables are now named rather sensibly, highlighting their roles. Note also that this logic does not quite determine that s1 is an anagram of s2 (consider e.g. "abb" and "aab"), but this is in fact what you were doing.
Since this looks like homework, I'll leave it up to you to try to figure out when two strings are anagrams.
See also
Java Coding Conventions
Java Language Guide/For-each loop
Java Tutorials/Collections Framework
Related questions
Why doesn't Java Map extends Collection?
Make sure that the file lies in the same directory as your .class file. It doesn't matter if it is in the same as your .java file or not.
Other than that, the only problem I can see is in your indentation, which doesn't matter.
The normal practice is to put resources in the runtime classpath or to add its path to the runtime classpath so that you can get it by the classloader. Using relative paths in Java IO is considered poor practice since it breaks portability. The relative path would be dependent on the current working directory over which you have totally no control from inside the Java code.
After having placed it in the classpath (assuming that it's in the same folder as the Java class itself), just do so:
BufferedReader reader = null;
try {
InputStream input = Practice.class.getResourceAsStream("anagrams.in");
reader = new BufferedReader(new InputStreamReader(input, "UTF-8")); // Or whatever encoding it is in.
// Process it.
// ...
} finally {
if (reader != null) try { reader.close(); } catch (IOException ignore) {}
}
Closing in finally is by the way mandatory to release the lock on the file after reading.
Put the anagrams.in file in the same location as the .class file. Then you will be able to read the file. And this should help you get some links on how to read from files in Java.

Categories

Resources