2

Let's say I have a java source file (source.java) same as below.

1 package demo;
2
3 public class Source {
4
5 public static void main(String[] args) {
6
7 String sample = "foo bar";
8 
9 System.out.println(sample.length());
10
11 }
12 }

Now i want to write a java code that reads this source file line by line and when it encounters the sample variable in 9th line, it will give me which class (i.e java.lang.String) the sample variable belongs. How can i do that? I have already seen the link below, which doesn't work for me as it prints the type name in the same source file.

Print the type of a Java variable

ifeomaro
2,7633 gold badges29 silver badges33 bronze badges
asked Jun 30, 2018 at 19:06
9
  • 1
    If I understand your question, you're asking for raw text to be parsed as code. This isn't a trivial problem--have you tried writing any code to do this yet? If so, please post it with the area you're running into trouble in. Commented Jun 30, 2018 at 19:10
  • 2
    Do you actually need to treat the java source as raw text? I would use reflection for this, much simpler, IF you are open to not having support for local variables. See here: stackoverflow.com/questions/6816951/… Commented Jun 30, 2018 at 19:17
  • 1
    You'd need to identify where in the code the variable is declared, then extract the name of its type, then work out what type that name refers to. All in all, definitely non-trivial. Of course, you can use the compiler API, but I wouldn't even say that makes it especially easy. Commented Jun 30, 2018 at 19:28
  • @ggorlen I haven't tried anything yet. Was thinking about the solution. Will post something if i get into trouble implementing it. Commented Jun 30, 2018 at 20:06
  • @MarDev Yeah, i need to treat the java source as raw text , reflection doesn't work for me :( Commented Jun 30, 2018 at 20:07

2 Answers 2

5

For reading a standalone java source file, you have to start from scratch, read line by line, parse each word of file following the Java's syntax,... too many works to do. Or I recommend JavaParser.

JavaParser reads and parses the raw Java source file to a java object that you can retrieve the information.

This is the sample code for your problem:

public String getSampleVariableType() throws Exception {
 // Use the raw text of file as input
 // `CompilationUnit` contains all information of your Java file.
 CompilationUnit compilationUnit = JavaParser.parse("package demo;\n" +
 "\n" +
 "public class Source {\n" +
 " public static void main(String[] args) {\n" +
 " String sample = \"foo bar\"; \n" +
 " System.out.println(sample.length());\n" +
 " }\n" +
 "}");
 // Find class by name
 ClassOrInterfaceDeclaration clazz = compilationUnit.getClassByName("Source")
 .orElse(null);
 if (clazz == null) 
 throw new ClassNotFoundException();
 // Find method by name
 List<MethodDeclaration> methods = clazz.getMethodsByName("main");
 if (methods.size() == 0) 
 throw new MethodNotFoundException();
 // Get the content of method's body
 MethodDeclaration method = methods.get(0);
 BlockStmt block = method.getBody().orElse(null);
 if (block == null) 
 throw new MethodEmptyException();
 // Statement `String sample = "foo bar";` is a VariableDeclaration.
 // Find all VariableDeclaration in current method, filter as you want
 // and get its class type by using `getType()` method
 return block.findAll(VariableDeclarator.class).stream()
 .filter(v -> v.getName().asString().equals("sample"))
 .map(v -> v.getType().asString())
 .findFirst().orElse(null);
}

The result is the simple name of type: String.

In order to represent the result as a fully qualified name (i.e java.lang.String) . You may need find all ImportDeclaration and find the imported name:

public static String getFullyQualifiedName(CompilationUnit cu, String simpleName) {
 return cu.findAll(ImportDeclaration.class).stream()
 .filter(i -> i.getName().asString().matches(".*\\b" + simpleName + "\\b"))
 .map(i -> i.getName().asString())
 .findFirst().orElse("java.lang." + simpleName);
}
answered Jul 1, 2018 at 7:03
1
  • This looks promising. I'll now run this code on my machine. I'll accept it as an answer if it solves my mentioned problem. Commented Jul 1, 2018 at 7:59
0

Here is a not complete(some TODO items) implements of trying to parse it with regex, we can try it with #luckFindInScope() first, if failed, then try the complex way with the complex source code.

package com.mytest.core.share;
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
public class TestParse {
 public static void main(String[] args) throws Exception {
 //testHit();
 testNotHit();
 }
 public static void testNotHit() throws Exception {
 // input position
 int position = 511;
 String id = "sample";
 parse(position, id);
 }
 public static void testHit() throws Exception {
 // input position
 int position = 955;
 String id = "sample";
 parse(position, id);
 }
 public static void parse(int position, String id) throws Exception {
 int end = position;
 FileInputStream fs = new FileInputStream(new File("TestFile.java"));
 String source = IOUtils.toString(fs, "UTF-8");
 source = source.substring(0, end);
 System.out.println("### look from" +source);
 // remove all String in source code;
 source = removeStringInSource(source);
 int start = 0;
 int stack = 0;// for nested scope
 boolean hit = false;
 // find the char '{' from the end of source
 for (int i = source.length(); i > 0; i--) {
 String current = source.substring(i - 1, i);
 if (stack == 0) {
 if (current.equals("{")) {
 start = i;// lookup from start to end;
 System.err.println("from this {, start to search from the location at " + i);
 hit = findInScope(source, id, start, end);
 end = start; // skip, search next scope;
 if (hit) {
 break;
 } else {
 continue;
 }
 }
 }
 // skip bracket pair {}
 if (current.equals("}")) {
 stack++;
 end = i;// skip it;
 }
 if (current.equals("{")) {
 stack--;
 end = i;// skip it;
 }
 }
 if (hit == false) {
 // TODO: find the type in the class members and super class members
 }
 // TODO: find full class name in the java.lang.* or in the header of the source
 // of import section.
 }
 private static boolean findInScope(String source, String id, int start, int end) {
 String regex = "[A-Za-z0-9_$]+\\s+" + id;
 String text = source.substring(start, end);
 Matcher matcher = Pattern.compile(regex).matcher(text);
 boolean result = matcher.find();
 if (result) {
 hitString = matcher.group();
 System.err.println("hitString = " + hitString + " ,in the text scope: " + text);
 }
 return result;
 }
 private static boolean luckFindInScope(String source, String id) {
 String regex = "[A-Za-z0-9_$]+\\s+" + id;
 Matcher matcher = Pattern.compile(regex).matcher(source);
 int count = 0;
 while (matcher.find()) {
 count++;
 hitString= matcher.group();
 }
 return count == 1;
 }
 private static String hitString = "";
 // fill star * in the string value
 private static String removeStringInSource(String input) {
 Matcher matcher = Pattern.compile("\".*?\"").matcher(input);
 String match = "";
 while(matcher.find()) {
 match = matcher.group();
 char[] symbols = new char[match.length()];
 Arrays.fill(symbols, '*');
 input = input.replace(match, new String(symbols));
 }
 System.out.println("$$ removed: " + input);
 return input;
 }
}

It is more complex source code with below:

package com.mytest.test.parse;
public class TestFile {
 public static void main(String[] args) {
 String sample = "foo bar";
 System.out.println(sample.length());
 }
 public static String sample = null;
 public static void test1() {
 if (sample != null) {
 {
 String sample = "foo bar";
 System.out.println(sample);
 {
 sample = "foo bar";
 System.out.println(sample);
 }
 }
 {
 System.out.println(sample);
 int test = 0;
 {
 if (test >= 0) {
 sample = "foo bar";
 System.out.println(sample);
 String sample = "foo bar";
 System.out.println(sample);
 }
 }
 }
 if(sample.equals("foo bar")) {
 System.out.println(sample);
 }
 }
 if( sample == null) {
 String sample = "foo bar";
 if(sample.equals("foo bar")) {
 System.out.println(sample);
 }
 }
 }
 public static void test2{
 if( sample == null) {
 String sample = "foo bar";
 if(sample.equals("foo bar")) {
 System.out.println(sample);
 }
 }
 }
}
answered Apr 3, 2019 at 16:11

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.