I have one problem in understanding of Java 8 associated with reference to methods as a parameter to static methods. There is my code where I can't find how to send a reference to a method which doesn't have any parameters and must be a method for an object of the definite class.
So, I want that the method which I send to static function may be used for the object in the static method.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
interface FuncForPath<T> {
T func(T path);
}
class MethodsForFolder {
public static void printFilesInFolder(Path path, FuncForPath<Path> funcPath) {
Stream<Path> paths = null;
try {
paths = Files.list(path);
} catch (IOException e) {
System.out.println(
"The problems have been appeared during reading folder: " + path.toAbsolutePath().toString());
e.printStackTrace();
}
System.out.println("Files list in folder:" + path.toAbsolutePath().toString());
paths.forEach(p -> funcPath.func(p).toString()); // I don't know to how to write this code to perform
}
}
public class TestRefToIntance {
public static String testWindowsFloder = "C://Logs";
public static void main(String[] args) {
Path path = Paths.get(testWindowsFloder);
// I want that this 2 methods are performed depending on transfered methods
// reference
MethodsForFolder.printFilesInFolder(path, Path::toAbsolutePath);
MethodsForFolder.printFilesInFolder(path, Path::getFileName);
}
}
Try Function<Path, Path> instead of FuncForPath. This will require a method taking a Path parameter and returning a Path. Note that instance methods always have an "invisible" this parameter, hence Path::getFileName matches that signature.
You'd then call it like this: paths.forEach( p -> funcPath.apply( p ).toString() ); (although you're not doing anything with the returned string, so you probably want to call paths.map( f ).map( Path::toString ).collect( someCollector ); instead.
How I understand I have resolved this problem. There is code below.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
interface FuncForPath<T> {
T func(T path);
}
class MethodsForFolder {
public static void printFilesInFolder(Path path, FuncForPath<Path> funcPath) {
Stream<Path> paths = null;
try {
paths = Files.list(path);
} catch (IOException e) {
System.out.println(
"The problems have been appeared during reading folder: " + path.toAbsolutePath().toString());
e.printStackTrace();
}
System.out.println("Files list in folder:" + path.toAbsolutePath().toString());
paths.forEach(p -> System.out.println(funcPath.func(p).toString())); // I don't know to how to write this code to perform
}
}
public class TestRefToIntance {
public static String testWindowsFloder = "C://Logs";
public static void main(String[] args) {
Path path = Paths.get(testWindowsFloder);
// I want that this 2 methods are performed depending on transfered methods
// reference
MethodsForFolder.printFilesInFolder(path, Path::toAbsolutePath);
MethodsForFolder.printFilesInFolder(path, Path::getFileName);
}
}
Also, I have deleted the generic specification in interface description.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
interface FuncForPath { // there is interface without type <T>
Path func(Path path);
}
class MethodsForFolder {
public static void printFilesInFolder(Path path, FuncForPath funcPath) {
Stream<Path> paths = null;
try {
paths = Files.list(path);
} catch (IOException e) {
System.out.println(
"The problems have been appeared during reading folder: " + path.toAbsolutePath().toString());
e.printStackTrace();
}
System.out.println("Files list in folder:" + path.toAbsolutePath().toString());
paths.forEach(p -> System.out.println(funcPath.func(p).toString())); // I don't know to how to write this code
// to perform
}
}
public class TestRefToIntance {
public static String testWindowsFloder = "C://Logs";
public static void main(String[] args) {
Path path = Paths.get(testWindowsFloder);
// I want that this 2 methods are performed depending on transfered methods
// reference
MethodsForFolder.printFilesInFolder(path, Path::toAbsolutePath);
MethodsForFolder.printFilesInFolder(path, Path::getFileName);
}
}
Related
I have difficulty in accessing member of a class with the parameter using a collection. For example List. This is a tiny example of a "grabber" class, which accesses a function with int parameter, but cannot find the function with the collection as its argument. This code prints a list of all members and their arguments as a diagnosis.
import static java.lang.System.out;
import static java.lang.System.err;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
public class grabber
{
public static void main(String args[])
{
Class<?> c=null;
try {
c=Class.forName("item");
} catch (ClassNotFoundException e) {
out.printf("?W: class item not found\n");
return;
}
try {
Object an_item=c.getDeclaredConstructor().newInstance();
out.printf("Listing methods of 'item' and their parameters\n");
Method[] allm=c.getDeclaredMethods();
for (Method m: allm)
{
String mnam=m.getName();
Type[] mptype=m.getGenericParameterTypes();
out.printf("\n\tItem Method m=%s lg of param types:%d\n",mnam, mptype.length);
for (Type t:mptype)
{
out.printf("\t arg type=%s\n",t.toString());
}
}
out.println();
Method callf2=c.getDeclaredMethod("f2", int.class);
out.printf("\tMethod callf2() is : %s\n", callf2);
Object ret=callf2.invoke(an_item,234);
List<Object> arlob=new ArrayList<Object>(); // identical to item.f1 argument
Method callf1=c.getDeclaredMethod("f1",arlob.getClass()); // this causes exception
} catch (Exception e) {
err.println("?E: Exception "+e.getClass().getName());
err.println("?E: Exception "+e.getLocalizedMessage());
e.printStackTrace(err);
return;
}
} /* main() */
} /* class */
The called class "item" is simply:
import java.util.List;
class item
{
List<Object> f1(List<Object> lob)
{
System.out.printf("\t\t------item.f1: lob.lg=%d\n",lob.size());
return lob;
}
int f2(int arg)
{
System.out.printf("\t\t------item.f2: arg=%d\n",arg);
return arg;
}
}
When I run the class grabber, the following happens, and I could not discover a way to access the function f1(List):
Listing methods of 'item' and their parameters
Item Method m=f2 lg of param types:1
arg type=int
Item Method m=f1 lg of param types:1
arg type=java.util.List<java.lang.Object>
Method callf2() is : public int item.f2(int)
------item.f2: arg=234
?E: Exception java.lang.NoSuchMethodException
?E: Exception item.f1(java.util.ArrayList)
java.lang.NoSuchMethodException: item.f1(java.util.ArrayList)
at java.lang.Class.getDeclaredMethod(Class.java:2130)
at grabber.main(grabber.java:51)
I wonder if someone found out how to access functions using abstract classes in argument list.
Simply solution would be to put known type as you already know what method is accepting as you have done in the first method call.
Change
Method callf1=c.getDeclaredMethod("f1",arlob.getClass());
to
Method callf1=c.getDeclaredMethod("f1",List.class);
I'm trying to Parse the static variable value from the JAVA file. But couldn't be able to parse the variable.
I've used JavaParser to Parse the code and fetch the value of variable. I got success in fetching all other class level variable and value but couldn't be able to parse the static field.
The Java File looks like ...
public class ABC {
public string variable1 = "Hello How are you?";
public boolean variable2 = false;
public static String variable3;
static{
variable3 = new String("Want to Fetch this...");
}
//Can't change this file, this is input.
public static void main(String args[]){
//....Other Code
}
}
I'm able to parse the all variables value except "variabl3". The Code of Java File looks like above Java Code and I need to Parse "variable3"'s value.
I've done below code to parse the class level variable...
import java.util.HashMap;
import java.util.List;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
public class StaticCollector extends
VoidVisitorAdapter<HashMap<String, String>> {
#Override
public void visit(FieldDeclaration n, HashMap<String, String> arg) {
// TODO Auto-generated method stub
List <VariableDeclarator> myVars = n.getVariables();
for (VariableDeclarator vars: myVars){
vars.getInitializer().ifPresent(initValue -> System.out.println(initValue.toString()));
//System.out.println("Variable Name: "+vars.getNameAsString());
}
}
}
Main Method ...
public class Test {
public static void main(String[] args) {
File file = new File("filePath");
CompilationUnit compilationUnit = null;
try {
compilationUnit = JavaParser.parse(file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
HashMap<String, String> collector = new HashMap<String, String>();
compilationUnit.accept(new StaticCollector(), collector);
}
}
How could I parse the value of "variable3", which is static and value assigned inside static block? There might be other variable in the code but I need to find value of particular variable value (in this case Variable3).
Am I doing something wrong or i need to add some other way, please suggest.
Inspecting the AST as something that's easily readable, e.g., a DOT (GraphViz) image with PlantUML is a huge help to solve this kind of problem. See this blog on how to generate the DOT as well as other formats.
Here's the overview, with the "variable3" nodes highlighted (I just searched for it in the .dot output and put a fill color). You'll see that there are TWO spots where it occurs:
Zooming in on the node space on the right, we can see that the second sub-tree is under an InitializerDeclaration. Further down, it's part of an AssignExpr where the value is an ObjectCreationExpr:
So, I adapted your Visitor (it's an inner class to make the module self contained) and you need to override the visit(InitializerDeclaration n... method to get to where you want:
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.InitializerDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.List;
public class Test {
public static void main(String[] args) {
File file = new File("src/main/java/ABC.java");
CompilationUnit compilationUnit = null;
try {
compilationUnit = StaticJavaParser.parse(file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
HashMap<String, String> collector = new HashMap<String, String>();
compilationUnit.accept(new StaticCollector(), collector);
}
private static class StaticCollector extends
VoidVisitorAdapter<HashMap<String, String>> {
#Override
public void visit(FieldDeclaration n, HashMap<String, String> arg) {
List<VariableDeclarator> myVars = n.getVariables();
for (VariableDeclarator vars: myVars){
vars.getInitializer().ifPresent(initValue -> System.out.println(initValue.toString()));
//System.out.println("Variable Name: "+vars.getNameAsString());
}
}
#Override
public void visit(InitializerDeclaration n, HashMap<String, String> arg) {
List<Statement> myStatements = n.getBody().getStatements();
for (Statement s: myStatements) {
s.ifExpressionStmt(expressionStmt -> expressionStmt.getExpression()
.ifAssignExpr(assignExpr -> System.out.println(assignExpr.getValue())));
}
}
}
}
Here's the output showing additionally variable3's initialization in the static block:
"Hello How are you?"
false
new String("Want to Fetch this...")
From what i know, java cannot retrieve anything inside method. so i using option -g or -g:vars in javac.
for e.g :
class Test {
int a=0;
void method(boolean boo){
String b;
try
{
new Thread().sleep(1000);
}
catch(InterruptedException e){}
JOptionPane.showMessageDialog(null,"test");
BufferedImage image=ImageIO.read(new File("C:\\file.png"));
}
}
So, i use BCEL to retrieve local variable.
import org.apache.bcel.classfile.*;
import org.apache.bcel.Repository;
class javap
{
public static void main(String[]args)
{
try
{
JavaClass jc = Repository.lookupClass("test");
ConstantPool constantPool = jc.getConstantPool();
Method [] method=jc.getMethods();
for (Method m : method)
{
LocalVariableTable lvt=m.getLocalVariableTable();
LocalVariable[] lv=lvt.getLocalVariableTable();
for(LocalVariable l : lv)
{
System.out.println(l.getName()+" : "+l.getSignature());
}
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
But it doesn't work if variable is not initialized like String b. Additionally I want to track constructor calls like new Thread() or new File() as well as invocations of static methods and inside intialize inside JFileChooser like new File and JOptionPane too. So I want to see in output Thread, String b, JOptionPane, ImageIO, and File.
What should I do, to make them are printed in my program?
You simply cannot get the b variable, because java compilers (at least javac and ecj) do not put it into the generated class file at all: if variable is not assigned, no variable slot is allocated and it's not stored in the LocalVariableTable. You can create unused variable with longer name like String blahblah;, compile the class, open the compiled .class-file in text editor and search for blahblah string. You will not found it. So BCEL cannot help you to find the variable which is absent.
If you want to track new objects creation and static methods invocation, you can do it scanning the method bytecode. The easiest way to do it with BCEL is to utilize the MethodGen (even though you don't want to generate the new method). Here's the full code:
import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.ConstantMethodref;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.NEW;
class javap
{
public static void main(String[]args)
{
try
{
JavaClass jc = Repository.lookupClass("Test");
ConstantPool constantPool = jc.getConstantPool();
Method [] method=jc.getMethods();
for (Method m : method)
{
LocalVariableTable lvt=m.getLocalVariableTable();
LocalVariable[] lv=lvt.getLocalVariableTable();
for(LocalVariable l : lv)
{
System.out.println(l.getName()+" : "+l.getSignature());
}
}
ConstantPoolGen cpg = new ConstantPoolGen(constantPool);
for(Method m : method)
{
MethodGen mg = new MethodGen(m, m.getName(), cpg);
for(InstructionHandle ih = mg.getInstructionList().getStart();
ih != null; ih = ih.getNext())
{
if(ih.getInstruction() instanceof NEW)
{
NEW newInst = ((NEW)ih.getInstruction());
String className = constantPool.getConstantString(
newInst.getIndex(), Constants.CONSTANT_Class);
System.out.println("Class instantiation: "+className);
}
if(ih.getInstruction() instanceof INVOKESTATIC)
{
INVOKESTATIC newInst = ((INVOKESTATIC)ih.getInstruction());
String className = constantPool.getConstantString(
((ConstantMethodref) constantPool
.getConstant(newInst.getIndex()))
.getClassIndex(),
Constants.CONSTANT_Class);
System.out.println("Static call: "+className);
}
}
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
The output is the following:
this : LTest;
this : LTest;
boo : Z
Class instantiation: java/lang/Thread
Static call: java/lang/Thread
Static call: javax/swing/JOptionPane
Class instantiation: java/io/File
Static call: javax/imageio/ImageIO
Note that you have java/lang/Thread twice, because new Thread() is catched as object creation and Thread.sleep() is catched as static method invocation.
From what i read on the Net: PDG or SDG can give me a tree of dependecies i tried with a simple exemple but i have no result
what i did :
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import com.graph.element.Node;
import com.graph.internal.NodeNotFoundException;
import com.graph.sdg.SystemDependenceGraph;;
public class A {
public static void main(String[] args) throws FileNotFoundException, IOException, AnalyzerException, NodeNotFoundException {
SystemDependenceGraph lvSystemDependenceGraph
=new SystemDependenceGraph("C:\\Users\\amina\\workspace\\SDG\\fact","C:\\Users\\amina\\workspace\\SDG\\fact\\bin\\Fact.class");
Iterator<Node> lvIterator =lvSystemDependenceGraph.controlDependenceBFSIterator();
while (lvIterator.hasNext()) {
Node lvNode = lvIterator.next();
}
}
}
class fact :
public class Fact {
public static void main(String[] args) {
int f;
int n;
n=4;
f=1;
while(n!=0){
f=f*n;
n=n-1;
}
System.out.println("f= "+f+" n= "+n);
}
}
when i run class A there is no result
SDG is a java library for analyzing java code. It processes the java sources/byte code, converts into a graph. If you iterate with either BFS or DFS, it gives you series of instruction(code) including the callee method instructions.
In the above example, Class A is iterating over the instructions. Every Node is a instruction there. After retrieving node, you are not printing it so there is no output for the above class.
If you add below line, then it works.
System.out.println("Instruction is " + node.getName());
There are other methods in the Node class like sourceline(getLine()), source is a caller or not (getCaller), what is the instruction type (getType()) etc...
With Java7 and Java8, I would like to generate a warning if some methods was called.
The warning will be print if a specific jar is present when then user compile.
I write an Annotation Processor and catch the visitMethodInvocation(). Now, I want extract the class and method names will be invoked.
Is it possible to do that ?
Or how to approach this?
You can do something like:
package mystuff;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.tools.*;
#SupportedAnnotationTypes("*")
public class Proc extends AbstractProcessor{
#Override
public boolean process(Set<?extends TypeElement>annotations,RoundEnvironment roundEnvironment){
final Trees trees=Trees.instance(processingEnv);
for(Element element:roundEnvironment.getRootElements()){
TreePath path=trees.getPath(element);
final CompilationUnitTree compilationUnit=path.getCompilationUnit();
compilationUnit.accept(new TreeScanner<Object,Object>(){
#Override
public Object visitMethodInvocation(MethodInvocationTree tree,Object data){
tree.getMethodSelect().accept(new SimpleTreeVisitor<Object,Object>(){
#Override
public Object visitMemberSelect(MemberSelectTree tree,Object data){
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,String.format("class: %1$s\nmethod: %2$s",tree.getExpression(),tree.getIdentifier()));
return null;
}
},null);
return null;
}
},null);
}
return true;
}
}
I used that processor to process the below class
package stuff;
import java.util.*;
#MyAnnotation
class MyProgram{
public void run(){
System.out.println("Hello World!");
}
}
and achieved this result:
class: System.out
method: println
I am pretty sure that the method name generated is what you are looking for. I am pretty sure that the "class" is not exactly what you are looking for, but is a pretty good start.
In my example you probably wanted it to print "java.io.PrintStream" for the class. To get that you could use processingEnv.getElementUtils().getTypeElement("java.lang.System") to get a TypeElement representing the system class. Then you can use processingEnv.getElementUtils().getAllMembers() to get every single member of the system class. Iterate through that to find out. Use the asType method to get its type.
The preceding paragraph was a gross simplification. The processor did not know a priori that out is a static member of a class that is part of the implicitly imported java.lang package. So your code will have to try and fail to find the following classes System and java.util.System (because it is in the imports), System.out, java.util.System.out, and java.lang.System.out.
I only dealt with MemberSelect. You will have to deal with other possibilities including MethodInvocation. For example new Object().toString().hashCode() should be class=Object, method=hashCode.
As an alternative to the great answer from #emory, you can consider using the pluggable type-checking annotation processing provided by the Checker Framework. The advantage is it can help you to easily determinate the type of the method invoker. Here is an example processor based on the checker framework (add checker.jar to the classpath when compile).
#SupportedAnnotationTypes("*")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyTypeProcessor extends AbstractTypeProcessor {
class MyTreePathScanner extends TreePathScanner<Void, Void> {
private final Trees trees;
private final TreePath root;
public MyTreePathScanner(TreePath root) {
this.trees = Trees.instance(processingEnv);
this.root = root;
}
#Override
public Void visitMemberSelect(MemberSelectTree node, Void aVoid) {
ExpressionTree expression = node.getExpression();
TreePath expr = TreePath.getPath(root, expression);
TypeMirror type = trees.getTypeMirror(expr);
Element typeElement = processingEnv.getTypeUtils().asElement(type);
Optional<? extends Element> invoker = typeElement.getEnclosedElements().stream().filter(
e -> e.getSimpleName().equals(node.getIdentifier())).findFirst();
if (invoker.isPresent() && invoker.get().getKind() == ElementKind.METHOD) {
System.out.println("Type: " + typeElement + ", method: " + invoker.get());
}
return super.visitMemberSelect(node, aVoid);
}
}
#Override
public void typeProcess(TypeElement typeElement, TreePath root) {
new MyTreePathScanner(root).scan(root, null);
}
}
Which is processing the following input source.
public class Test {
public void foo() {
}
public static void main(String[] args) {
System.out.println("Hello world!");
Test t = new Test();
t.foo();
}
}
Here is the output:
Type: java.io.PrintStream, method: println()
Type: Test, method: foo()