Line number counter using java AST parser - java

I have some interesting exercise - to read .java file with Java, to find every code part in this .java file(like methods and etc.) and to say how many line numbers we have in every method without whitespaces and comments and also to find the count of items in every method.
Here is some example that I use - so could Java Parser. With the code I can say how many line numbers I have in method, but cannot remove the count of whitespaces and comments. Can someone help me?
The code:
public static void main(String[] args) throws ParseException, IOException {
File f = new File(".").getAbsoluteFile();
File srcRoot = new File(f, "src");
String srcFilename = TestMethodLineNumber.class.getName().replaceAll("\\.", "/") + ".java";
File src = new File(srcRoot, srcFilename);
System.out.println(f);
System.out.println(srcRoot);
System.out.println(src);
getMethodLineNumbers(src);
}
private static void getMethodLineNumbers(File src) throws ParseException, IOException {
CompilationUnit cu = JavaParser.parse(src);
new MethodVisitor().visit(cu, null);
}
/**
* Simple visitor implementation for visiting MethodDeclaration nodes.
*/
private static class MethodVisitor extends VoidVisitorAdapter {
#Override
public void visit(MethodDeclaration m, Object arg) {
System.out.println("From [" + m.getBeginLine() + "," + m.getBeginColumn() + "] to [" + m.getEndLine() + ","
+ m.getEndColumn() + "] is method:");
System.out.println(m);
}
}
}

It looks to me like JavaParser already removes blank lines, I would use a technique from this thread Removing all types of comments in java file to remove all the comments and then use JavaParser to get your results.
In general questions like these aren't always interesting unless you're actually going after executable code. Consider:
if (a)
{
runA();
}
else
{
runB();
}
You're count will be 8, what exactly does that tell you? Is it more interesting to know there is one if statement and/or two method calls? You'll really want better constraints to know what you're going after:
a ? runA() : b ? runB(): runError("nothing true");
Is only one line, but you have 3 potential execution branches. You can also run into some pretty crazy statements that span multiple lines.
MyFancyObject myFancyObject = new MyFancyObject("This is my really long name",
123113212,
"2014-11-20 18:43:12",
anotherArg, yetAnother,
moar);
Should this be 5 lines or 1?

Related

How can i make a moving file who gets renamed if in the target directory there is a file whit the same name(java)?

I'm writing a moving file program for my pc, to finish it I need to rename a moving file if in the target directory there is already a file named the same, how can i do that?
One could use File.move and use CopyOptions. But it seems more elegant to check the existence of the target.
void move(Path source, Path target) throws IOException {
int fileno = 0;
while (Files.exist(target)) {
target = ...
++fileno;
}
// Files.createDirectories(target.getParent());
Files.move(source, target);
}
In java the convention is that the target is not a directory (the new parent).
It's a programming language; you would program the behaviour you want. There is nothing 'baked in', if that's what you were thinking.
As with most things that could potentially interact with other threads or applications running simultaneously, you don't check-and-act - because other apps or threads could change the situation in between your 'check' and your 'act' - instead, you act-then-check: You just do the thing, and catch exceptions that indicate the thing cannot be done due to failing checks, and act accordingly. In other words, don't do something like:
while (file.exists()) file = incrementSomething();
writeTo(file);
That's check-then-act. Don't do that.
Instead you'd first just move it, and then catch the exception that indicates that it cannot be moved because the 'target' already exists and you did not specify that overwriting is acceptable.
It's your program, you decide what you want to do when this happens. Perhaps you want to append (1) to the file name and try again, then (2), and so on, all the way to (999) and once you get that far, just stop and crash. Or perhaps you want to roll up a random sequence of 6 characters and append that, which is a heck of a lot faster if this 'naming conflict' scenario is likely. That's the joy of programming: Anything's possible, you decide what you want and then you write it just like that.
Let's say you want the (1) thing. It would look something like:
public void move(Path sourceFile, Path targetDir) throws IOException {
try {
Files.move(sourceFile, targetDir.resolve(sourceFile.getFileName()));
} catch (FileAlreadyExistsException e) {
String baseName = sourceFile.getFileName().toString();
int lastDot = baseName.lastIndexOf('.');
String prefix = lastDot == -1 ? baseName : baseName.substring(0, lastDot);
String suffix = lastDot == -1 ? "" : baseName.substring(lastDot);
moveIncremental(sourceFile, targetDir, prefix, suffix, 1);
}
}
private void moveIncremental(Path sourceFile, Path targetDir, String prefix, String suffix, int counter) throws IOException {
Path target = targetDir.resolve(prefix + " (" + counter + ")" + suffix);
try {
Files.move(sourceFile, target);
} catch (FileAlreadyExistsException e) {
if (counter >= 999) throw e; // Give up after 1000 increments.
moveIncremental(sourceFile, targetDir, prefix, suffix, counter + 1);
}
}
This code first just tries to move it, and responds to the error of 'no can do; there is a file already there' and then breaks the file into a prefix and suffix, i.e. something.txt becomes prefix = "something" suffix = ".txt" and will then call a helper method that tries to move it to something (1).txt, all the way up to 999.
It's just one of many ways to do this - as I said, your choice.

Method count words in a file

Hi guys I'm writing a method which counts words in a file, but apparently there is a mistake somewhere in the code and the method does not work. Here's my code:
public class Main2 {
public static void main(String[] args) {
count("/home/bruno/Desktop/WAR_JEE_S_09_Podstawy/MojPlik");
}
static int count(String fileName){
Path path = Paths.get(fileName);
int ilosc = 0;
String wyjscie = "";
try {
for (String charakter : Files.readAllLines(path)){
wyjscie += charakter;
}
StringTokenizer token = new StringTokenizer(wyjscie," \n");
} catch (IOException e) {
e.printStackTrace();
}
return ilosc;
}
}
The file path is correct, here is the file content
test test
test
test
after i call the method in main it displays nothing. Where is the mistake ?
Your code would count lines in a file ... well, if you followed up on that thought.
Right ow your code is simply reading lines, putting them into one large string, to then do nothing about the result of that operation. You have a single int counter ... who is init'ed to 0, and then just returned without ever being used/increased! And unless I am mistaken, readAllLines() will automatically remove the newline char in the end, so overall, your code is nothing but useless.
To count words you have to take each line and (for example) split that one-line-string for spaces. That gives you a number. Then add up these numbers.
Long story short: the real answer here is that you should step back. Don't just write code, assuming that this will magically solve the problem. Instead: first think up a strategy (algorithm) that solves the problem. Write down the algorithm ideas using a pen and paper. Then "manually" run the algorithm on some sample data. Then, in the end, turn the algorithm into code.
Also, beside that you does not output anything, there is a slight error behind you logic. I have made a few changes here and there to get your code working.
s.trim() removes any leading and trainling whitespace, and trimmed.split("\\s+") splits the string at any whitespace character, including spaces.
static int count(String fileName) throws IOException {
Path path = Paths.get(fileName);
int count = 0;
List<String> lines = Files.readAllLines(path);
for (String s : lines) {
String trimmed = s.trim();
count += trimmed.isEmpty() ? 0 : trimmed.split("\\s+").length;
}
return count;
}
Here is the code using functional-style programming in Java 8. This is also a common example of using Stream's flatMap - may be used for counting or printing words from a file.
long n = Files.lines(Paths.get("test.txt"))
.flatMap(s -> Stream.of(s.split("\\s+")))
.count();
System.out.println("No. of words: " + n);
Note the Files.lines(Path) returns a Stream<String> which has the lines from the input file. This method is similar to readAllLines, but returns a stream instead of a List.

Strange return value from java String.split, not sure why

My program reads a custom generated string which follows this pattern:
#INT{0,1,2}/STRING/LONG#INT{0,1,2}/STRING/LONG#.......#
So that's a hash at the start and end of the string, and seperating each substring, with each substring containing an int from 0-2, a string name, and a long value (taken from system clock). These subvalues are seperated by forward slashes.
My program successfully splits the main string on the hashes in the function 'splitOnHash', and assigns these to an array list of strings, which I can print out, and this seems to work nicely.
Next, in the second function, I iterate through this new arraylist, I want to split each substring on the '/' character, and stick each value into a custom 'HesitationEvent' object. Here is my code:
package sg;
import java.util.ArrayList;
import java.util.List;
import sg.events.HesitationEvent;
public class HesitationDataReader {
private static List<String> substrings = new ArrayList<String>();
private static List<HesitationEvent> events = new ArrayList<HesitationEvent>();
private static void splitOnHash(String HesData) {
for (String ret : HesData.split("#")) {
if (ret!= "") substrings.add(ret);
}
}
private static void createEventObjects() {
int code;
String object;
long time;
int c = 1;
for (String sub : substrings) {
System.out.println("count " + c);
c++;
String[] comp = sub.split("/");
System.out.println("comp:"+comp+":comp");
code = Integer.parseInt(comp[0]);
object = comp[1];
time = Long.parseLong(comp[2]);
HesitationEvent hes = new HesitationEvent(code, object, time);
events.add(hes);
}
}
public static void main (String args[]) {
String ourString = "#0/diamonds4/1392748304285#2/diamonds4/1392748304333#0/hearts7/1392748304364#2/hearts7/1392748305035#" +
"1/deck/1392748305456#1/deck/1392748311696#1/deck/1392748313489#1/deck/1392748315490#0/clubs7/1392748317599#" +
"2/clubs7/1392748317623#0/clubs5/1392748317623#2/clubs5/1392748317647#0/spades3/1392748317647#2/spades3/1392748323913#" +
"1/spades3/1392748324616#2/spades3/1392748324710#0/diamonds4/1392748324710#2/diamonds4/1392748324734#0/clubs5/1392748324782#" +
"2/clubs5/1392748325126#2/clubs5/1392748325214#1/clubs5/1392748325625#2/clubs5/1392748325782#0/spades6/1392748325806#" +
"2/spades6/1392748325918#0/spades3/1392748326006#2/spades3/1392748326262#0/diamonds4/1392748326262#2/diamonds4/1392748326678#" +
"2/diamonds4/1392748326830#1/diamonds4/1392748327498#2/diamonds4/1392748328094#0/spades6/1392748328118#2/spades6/1392748328206#" +
"0/diamonds13/1392748328238#2/diamonds13/1392748328534#0/diamonds13/1392748328790#2/diamonds13/1392748329046#0/hearts7/1392748329582#" +
"2/hearts7/1392748329942#0/hearts7/1392748330150#2/hearts7/1392748330246#0/hearts7/1392748330454#2/hearts7/1392748330654#" +
"1/deck/1392748333057#0/spades10/1392748333990#2/spades10/1392748334006#0/clubs13/1392748334006#2/clubs13/1392748334038#" +
"0/hearts1/1392748334038#2/hearts1/1392748334477#1/hearts1/1392748334927#2/hearts1/1392748335093#0/diamonds13/1392748335261#" +
"2/diamonds13/1392748335325#0/hearts7/1392748335341#2/hearts7/1392748335797#2/hearts7/1392748336013#2/hearts7/1392748336237#" +
"2/hearts7/1392748336325#2/hearts7/1392748336429#1/hearts7/1392748337240#2/hearts7/1392748337517#0/clubs4/1392748337525#" +
"2/clubs4/1392748337557#0/diamonds4/1392748337565#2/diamonds4/1392748337573#0/clubs5/1392748337573#2/clubs5/1392748337581#" +
"0/hearts6/1392748337581#2/hearts6/1392748337589#0/spades6/1392748337589#2/spades6/1392748337613#0/diamonds13/1392748337629#" +
"2/diamonds13/1392748337637#0/spades10/1392748337653#2/spades10/1392748337661#0/spades10/1392748337933#2/spades10/1392748337965#" +
"0/clubs13/1392748337965#2/clubs13/1392748338509#2/clubs13/1392748338557#1/clubs13/1392748338919#2/clubs13/1392748339237#" +
"1/deck/1392748341879#0/clubs13/1392748342477#2/clubs13/1392748342549#0/spades6/1392748345549#2/spades6/1392748345581#" +
"0/hearts1/1392748345637#2/hearts1/1392748345837#0/hearts1/1392748346421#2/hearts1/1392748346661#0/hearts9/1392748350302#" +
"2/hearts9/1392748350381#0/spades11/1392748350381#2/spades11/1392748350381#0/hearts2/1392748350381#2/hearts2/1392748350397#";
splitOnHash(ourString);
//for (String s:substrings) {
// System.out.println(s);
//}
createEventObjects();
}
}
Please ignore the variable c, and the for loop at the bottom, I used c to determine at what point the for loop crashes (the very first iteration). The for loop at the bottom (commented out) was used to confirm my splitOnHash function returned what I expected (it seems to work fine).
So essentially I am trying to split each substring into an array, and then pull and convert each value to the right type. I've also tried with split("/",3) to return exactly 3 values, but it gets the same result, which is:
count 1
Exception in thread "main" comp:[Ljava.lang.String;#1fae3c6:comp
java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at sg.HesitationDataReader.createEventObjects(HesitationDataReader.java:30)
at sg.HesitationDataReader.main(HesitationDataReader.java:87)
It's hard to tell here, but the "comp:[Ljava.lang.String;#1fae3c6:comp" is actually output of the program which appears in the midst of the exception for some reason. This is from the line where I try to print out the value of comp, surrounded by comp:VALUE:comp (I did this in case it prints an empty or null string, so I can see there is no gap between the colons.
So for some reason, in the first iteration, the output of sub.split into the String[] comp produces "Ljava.lang.String;#1fae3c6", when I'm expecting it to come up with something more like this: "0 diamonds4 1392748304285".
I just changed the code to try and access a location in the array rather than printing all 3 segments, and I get this error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
at sg.HesitationDataReader.createEventObjects(HesitationDataReader.java:29)
at sg.HesitationDataReader.main(HesitationDataReader.java:89)
There is obviously something wrong here, but I can't see the problem! I cannot use the original method I used to split the hashes, because I'm not returning strings in any order this time, I want to access specific values and convert them to specific types.
Thanks in advance for any help!
Here is the problem:
(ret!= "")
Should be:
if (!ret.equals(""))
I believe the error is causing insertion of empty strings.

How does the getAllSynonyms method workd in the rita word net package?

Can anyone explain to me how the 'getAllSynonyms' method i have used in the code below works? Every time I run it I get different results. Sometimes the words aren't synonyms at all! What is the significance of the middle argument in the call to the method? Links to any manuals that explain the methods of word net? Apologies if I have violated posting etiquette :).
import rita.wordnet.*;
import java.lang.reflect.Array;
import java.util.*;
public class Blah
{
RiWordnet wordnet;
String word;
void setup()
{
wordnet = new RiWordnet();
word = "car";
String[] poss = wordnet.getPos(word);
//System.out.println(poss)
for (int j = 0; j < poss.length; j++) {
System.out.println("\n\nSynonyms for " + word + " (pos: " + poss[j] + ")");
String[] synonyms = wordnet.getAllSynonyms(word,poss[0],10);
Arrays.sort(synonyms);
for (int i = 0; i < 5; i++) {
System.out.println(synonyms[i]);
}
}
}
public static void main(String args[])
{
Blah b=new Blah();
b.setup();
}
}
Documentation is here (just click the method names in the right column):
http://www.rednoise.org/rita/wordnet/documentation/
But I have to admit it is not very detailed.
When I run this, I repeatedly get
poss = [n]
synonyms = [chip, diode, microchip, thermistor]
If you don't, you may want to check if you have multiple versions of WordNet JARs in your classpath (the word lists are inside the Jar in a folder called rita/wordnet/wdict.dat)
You can use Arrays.toString() to easily print the contents of an array for debugging, without having to use a for loop every time.
The middle argument ('n') in this case is the word class ("a" = adjective, "n" = noun, "r" = adverb, "v" = verb) since some words (like "good") may exist in multiple classes and each of them has individual synonyms.
In the current version of RiTa, you can call randomizeResults(false); to disable this behavior (though you will now need to download WordNet itself separately):
import rita.*;
import java.util.*;
public static void main(String[] args)
{
RiWordNet rw = new RiWordNet("/WordNet-3.1"); // point to local installation
rw.randomizeResults(false); // don't randomize results
String[] s = rw.getAllSynonyms("car", "n");
System.out.println(Arrays.asList(s));
}

Programs that reproduces itself

Is it possible to make a Java program that prints its source code to a new file, and compiles it, and runs the compiled program?
Update:
Okay, might as well make it autorun. Enjoy the madness. Run at your own risk.
Yes it's possible, because I actually wrote it up. It doesn't do the RUN part (that's just too crazy, because as others have mentioned, it will cause an infinite loop), but here it is: Quine.java
import java.io.*;
public class Quine {
public static void main(String[] args) throws Exception {
char q = 34;
String out = "Quine$";
String text = (
"import java.io.*; " +
"public class [OUT] { " +
"public static void main(String[] args) throws Exception { " +
"char q = 34; String out = `[OUT]$`; String text = `[TEXT]`; " +
"PrintWriter pw = new PrintWriter(out + `.java`); " +
"pw.format(text, 34, out, text); " +
"pw.close(); Runtime runtime = Runtime.getRuntime(); " +
"runtime.exec(`javac ` + out + `.java`).waitFor(); " +
"runtime.exec(`java ` + out); " +
"} " +
"}"
).replace("`", "%1$c").replace("[OUT]", "%2$s").replace("[TEXT]", "%3$s");
PrintWriter pw = new PrintWriter(out + ".java");
pw.format(text, 34, out, text);
pw.close();
Runtime runtime = Runtime.getRuntime();
runtime.exec("javac " + out + ".java").waitFor();
runtime.exec("java " + out);
}
}
So here's how to get the craziness to start:
javac Quine.java to compile
java Quine to run it
It will produce, compile and run Quine$
I've made sure Quine.java is as readable as possible, so the major difference from Quine$.java are formatting and the 3x replace. The minor difference is that Quine$.java has out set to Quine$$.
Quine$ will produce, compile and run Quine$$
Quine$$ will produce, compile and run Quine$$$
Quine$$$ will produce, compile and run Quine$$$$
...
Do note that this doesn't do any reverse-engineering or cheat by reading the .java source code, etc. Quine is a quine-generator because it produces a different code differently formatted, but Quine$ is pretty much a true self-contained quine: it does reproduce itself, it just relabels it Quine$$ (which reproduces itself and relabels to Quine$$$ etc).
So technically there's no infinite loop: it will eventually come to a halt when the file system can't handle another $. I was able to manually stop the madness by forcefully deleting all Quine$* files, but run at your own risk!!!
Yes, it is possible.
A trivial implementation would be: have the source code contain itself in a string, save the string to a file and fill its own string with the same string (otherwise, the initial string would be of infinite size, due to the recursive manner of this implementation), compile the file, and run the compiled file (which will, in turn, do the very same).
Non-trivial implementations are significantly harder.
Sure it works - Have a look at rosetta code and navigate to Quine, which is a self-referential program that can, without any external access, output its own source.
There's one example for a quine in Java.
Programs that reproduces itself or Self Replicating Programs are known as Quine Programs
Sample Program in Java which reproduces itself.
public class QuineProgram {
public static void main(String[] args){
String f = "public class QuineProgram { "
+ "public static void main(String[] args)"
+ "{ String f =%c%s%1$c;"
+ " System.out.printf(f,34,f);}} ";
System.out.printf(f, 34, f);
}
}
Output:
public class QuineProgram { public static void main(String[] args){ String f ="public class QuineProgram { public static void main(String[] args){ String f =%c%s%1$c; System.out.printf(f,34,f);}} "; System.out.printf(f,34,f);}}
You could use the Java Compiler API (JSR-199) for this. Below, code from the JSR-199 that compiles code from a String (slightly modified to make it compile). The code actually compiles source code from the String into a byte array (i.e. it doesn't write to disk), loads it and then executes it via reflection:
MemoryFileManager.java: A file manager for compiling strings to byte arrays.
ByteArrayClassLoader.java: A class loader which loads classes from byte arrays.
CompileFromString.java: The class that wrap everything together.
That could be a starting point (credits to Peter Van der Ahé, the original author).
BTW, you need of course a JDK to use this API.
I don't know exactly what you want, but I think BeanShell is something you can use.
BeanShell is an interpreter. You can run uncompiled Java-code (So you give it a String with code and he runs it).
Of course if you really want to do what you wrote, the machine where the program is running needs a JDK to compile your program.
Hope this helps
I dont think it will work in Java. Wouldn't that involve overwriting a running class file.
Suppose your program is in Quine.java compiled to Quine.class.
Now Quine.class will attempt to write its output to Quine.java (so far so good), and compile it to Quine.class. This is gonna be a problem as Quine.class is already running
Yes - don't forget to use a JDK instead of a JRE:
Bundle the app's source code files with the app. The app would copy the source files to a new set of source code files, compile the new source files, bundle the new source code with the new class files into a new app, and then spawn the new app.
or
Bundle a decompiler with the app. The app would run the decompiler on its own class files to generate new source code files, compile the new source files, bundle the decompiler with the new class files into a new app, and then spawn the new app.
Here's a Java Quine using preview text block feature (-source 13 --enable-preview) where output is formatted the same as input:
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f ="""
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f =""%c%c%s%1$c"";
System.out.printf(f, 34, 10, f);
}
}
""";
System.out.printf(f, 34, 10, f);
}
}
Output:
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f ="""
package org.sample.quine;
public class QuineProgram
{
public static void main(String...args)
{
String f =""%c%c%s%1$c"";
System.out.printf(f, 34, 10, f);
}
}
""";
System.out.printf(f, 34, 10, f);
}
}
Heavily Commented Version:
package org.sample.quine;
public class Quine
{
public static void main(String...args)
{
// Inside text block use "" followed by token or token followed by "" so that we don't prematurely close text block
String f ="""
package org.sample.quine;
public class Quine
{
public static void main(String...args)
{
// Inside text block use "" followed by token or token followed by "" so that we don't prematurely close text block
String f =""%c%c%s%1$c"";
/* Tokens in template text block, each prefixed with percent symbol
* 1(c) third quote (34) of open block delimiter
* 2(c) new line (10) of open block delimiter
* 3(s) String f text block that goes between two text block delimiters
* 4(1$c) first quote (34) of close block delimiter,
* 1$ means first argument after template argument
* 2$ would be second argument after template argument
*/
// Arguments - 1 template (String f); 2 "; 3 newline; 4 template again without replacing tokens
System.out.printf(f, 34, 10, f);
}
}
""";
/* Tokens in template text block, each prefixed with percent symbol
* 1(c) third quote (34) of open block delimiter
* 2(c) new line (10) of open block delimiter
* 3(s) String f text block that goes between two text block delimiters
* 4(1$c) first quote (34) of close block delimiter,
* 1$ means first argument after template argument
* 2$ would be second argument after template argument
*/
// Arguments - 1 template (String f); 2 "; 3 newline; 4 template again without replacing tokens
System.out.printf(f, 34, 10, f);
}
}

Categories

Resources