I'd like to find the methods which changed between two arbitrary Java files.
What I've Tried
I've tried using diff (GNU diffutils 3.3) to find changes to lines in the file and diff --show-c-function connect those lines to the changed method. Unfortunately, in Java this lists the class, not the function.
I also tried git diff which seems to properly be able to find the changed function (at least as displayed on GitHub), but it doesn't always list the full signature and my files are not in the same Git repository (https://github.com/apache/commons-math/commit/34adc606601cb578486d4a019b4655c5aff607b5).
Desired Results
Input:
~/repos/d4jBugs/Math_54_buggy/src/main/java/org/apache/commons/math/dfp/Dfp.java
~/repos/d4jBugs/Math_54_fixed/src/main/java/org/apache/commons/math/dfp/Dfp.java
State of Files:
The changed methods between those two files are public double toDouble() and protected Dfp(final DfpField field, double x)
Output: (fully qualified names)
org.apache.commons.math.dfp.Dfp.toDouble()
org.apache.commons.math.dfp.Dfp(final DfpField field, double x)
Summary
Can I find the modified methods with the GNU diffutils tool or git diff and if yes, how would I do that? (Note: I'm not bound to those tools and am happy to install something else if needed.)
I used JavaParser 3.4.4, but it probably works but has not been tested with other versions.
It can be imported in Gradle with:
compile group: 'com.github.javaparser', name: 'javaparser-core', version: '3.4.4'
You can use my class like:
HashSet<String> changedMethods = MethodDiff.methodDiffInClass(
oldFileNameWithPath,
newFileNameWithPath
);
MethodDiff Source:
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.CallableDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.printer.PrettyPrinterConfiguration;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
/**
* Created by Loren Klingman on 10/19/17.
* Finds Changes Between Methods of Two Java Source Files
*/
public class MethodDiff {
private static PrettyPrinterConfiguration ppc = null;
class ClassPair {
final ClassOrInterfaceDeclaration clazz;
final String name;
ClassPair(ClassOrInterfaceDeclaration c, String n) {
clazz = c;
name = n;
}
}
public static PrettyPrinterConfiguration getPPC() {
if (ppc != null) {
return ppc;
}
PrettyPrinterConfiguration localPpc = new PrettyPrinterConfiguration();
localPpc.setColumnAlignFirstMethodChain(false);
localPpc.setColumnAlignParameters(false);
localPpc.setEndOfLineCharacter("");
localPpc.setIndent("");
localPpc.setPrintComments(false);
localPpc.setPrintJavadoc(false);
ppc = localPpc;
return ppc;
}
public static <N extends Node> List<N> getChildNodesNotInClass(Node n, Class<N> clazz) {
List<N> nodes = new ArrayList<>();
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration) {
// Don't go into a nested class
continue;
}
if (clazz.isInstance(child)) {
nodes.add(clazz.cast(child));
}
nodes.addAll(getChildNodesNotInClass(child, clazz));
}
return nodes;
}
private List<ClassPair> getClasses(Node n, String parents, boolean inMethod) {
List<ClassPair> pairList = new ArrayList<>();
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration) {
ClassOrInterfaceDeclaration c = (ClassOrInterfaceDeclaration)child;
String cName = parents+c.getNameAsString();
if (inMethod) {
System.out.println(
"WARNING: Class "+cName+" is located inside a method. We cannot predict its name at"
+ " compile time so it will not be diffed."
);
} else {
pairList.add(new ClassPair(c, cName));
pairList.addAll(getClasses(c, cName + "$", inMethod));
}
} else if (child instanceof MethodDeclaration || child instanceof ConstructorDeclaration) {
pairList.addAll(getClasses(child, parents, true));
} else {
pairList.addAll(getClasses(child, parents, inMethod));
}
}
return pairList;
}
private List<ClassPair> getClasses(String file) {
try {
CompilationUnit cu = JavaParser.parse(new File(file));
return getClasses(cu, "", false);
} catch (FileNotFoundException f) {
throw new RuntimeException("EXCEPTION: Could not find file: "+file);
}
}
public static String getSignature(String className, CallableDeclaration m) {
return className+"."+m.getSignature().asString();
}
public static HashSet<String> methodDiffInClass(String file1, String file2) {
HashSet<String> changedMethods = new HashSet<>();
HashMap<String, String> methods = new HashMap<>();
MethodDiff md = new MethodDiff();
// Load all the method and constructor values into a Hashmap from File1
List<ClassPair> cList = md.getClasses(file1);
for (ClassPair c : cList) {
List<ConstructorDeclaration> conList = getChildNodesNotInClass(c.clazz, ConstructorDeclaration.class);
List<MethodDeclaration> mList = getChildNodesNotInClass(c.clazz, MethodDeclaration.class);
for (MethodDeclaration m : mList) {
String methodSignature = getSignature(c.name, m);
if (m.getBody().isPresent()) {
methods.put(methodSignature, m.getBody().get().toString(getPPC()));
} else {
System.out.println("Warning: No Body for "+file1+" "+methodSignature);
}
}
for (ConstructorDeclaration con : conList) {
String methodSignature = getSignature(c.name, con);
methods.put(methodSignature, con.getBody().toString(getPPC()));
}
}
// Compare everything in file2 to what is in file1 and log any differences
cList = md.getClasses(file2);
for (ClassPair c : cList) {
List<ConstructorDeclaration> conList = getChildNodesNotInClass(c.clazz, ConstructorDeclaration.class);
List<MethodDeclaration> mList = getChildNodesNotInClass(c.clazz, MethodDeclaration.class);
for (MethodDeclaration m : mList) {
String methodSignature = getSignature(c.name, m);
if (m.getBody().isPresent()) {
String body1 = methods.remove(methodSignature);
String body2 = m.getBody().get().toString(getPPC());
if (body1 == null || !body1.equals(body2)) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(methodSignature.replace(" ", ""));
}
} else {
System.out.println("Warning: No Body for "+file2+" "+methodSignature);
}
}
for (ConstructorDeclaration con : conList) {
String methodSignature = getSignature(c.name, con);
String body1 = methods.remove(methodSignature);
String body2 = con.getBody().toString(getPPC());
if (body1 == null || !body1.equals(body2)) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(methodSignature.replace(" ", ""));
}
}
// Anything left in methods was only in the first set and so is "changed"
for (String method : methods.keySet()) {
// Javassist doesn't add spaces for methods with 2+ parameters...
changedMethods.add(method.replace(" ", ""));
}
}
return changedMethods;
}
private static void removeComments(Node node) {
for (Comment child : node.getAllContainedComments()) {
child.remove();
}
}
}
Related
I am trying to save a javafx.scene.shape.Path to a file (at least its elements) however since Path is non-serializable and its PathElement as well it has proven very difficult.
Could someone inform me of a way to either convert the object to a String (preferred), JSON or something else?
Here are all the ways I have tried saving the object:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import javafx.scene.shape.Path;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
class Test {
public static void main(String[] args) {
GsonBuilder builder = new GsonBuilder();
Path path;
Gson gson = builder.create();
{
Rectangle rectangle = new Rectangle(100, 100);
Polygon polygon = new Polygon(0, 0, 50, 50, 0, 50);
path = (Path) Shape.subtract(rectangle, polygon);
}
try {
String completePathObject = gson.toJson(path);
System.out.println(completePathObject);
} catch (IllegalArgumentException e) {
e.printStackTrace();
// java.lang.IllegalArgumentException: class com.sun.javafx.util.WeakReferenceQueue$ListEntry declares multiple JSON fields named next
}
try {
String pathObjectElements = gson.toJson(path.getElements());
System.out.println(pathObjectElements);
} catch (IllegalArgumentException e) {
e.printStackTrace();
// java.lang.IllegalArgumentException: class com.sun.javafx.util.WeakReferenceQueue$ListEntry declares multiple JSON fields named next
}
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.set"))) {
objectOutputStream.writeObject(path);
} catch (IOException e) {
e.printStackTrace();
// java.io.NotSerializableException: javafx.scene.shape.Path
}
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.set"))) {
objectOutputStream.writeObject(path.getElements());
} catch (IOException e) {
e.printStackTrace();
// java.io.NotSerializableException: javafx.scene.shape.Path$2
}
}
}
Nodes contain a lot of properties you'd need to convert to a form that can be written to a file.
Since your last attempt indicates you'll be satisfied with writing the path elements to the file, you could convert the PathElements to parts of a SVG path and also implement logic for parsing the elements PathElements from a svg path string.
The following doesn't accept all possible SVG paths and may accept some invalid paths:
public class SVGConverter {
private enum PathElementType {
ARC('a', ArcTo.class, ArcTo::new,
ArcTo::radiusXProperty,
ArcTo::radiusYProperty,
ArcTo::XAxisRotationProperty,
ArcTo::largeArcFlagProperty,
ArcTo::sweepFlagProperty,
ArcTo::xProperty,
ArcTo::yProperty),
CLOSE_PATH('z', ClosePath.class, ClosePath::new),
CUBIC_CURVE('c', CubicCurveTo.class, CubicCurveTo::new,
CubicCurveTo::controlX1Property,
CubicCurveTo::controlY1Property,
CubicCurveTo::controlX2Property,
CubicCurveTo::controlY2Property,
CubicCurveTo::xProperty,
CubicCurveTo::yProperty),
H_LINE_TO('h', HLineTo.class, HLineTo::new,
HLineTo::xProperty),
LINE_TO('l', LineTo.class, LineTo::new,
LineTo::xProperty, LineTo::yProperty),
MOVE_TO('m', MoveTo.class, MoveTo::new,
MoveTo::xProperty, MoveTo::yProperty),
QUAD_CURVE_TO('q', QuadCurveTo.class, QuadCurveTo::new,
QuadCurveTo::controlXProperty, QuadCurveTo::controlYProperty,
QuadCurveTo::xProperty, QuadCurveTo::yProperty),
V_LINE_TO('v', VLineTo.class, VLineTo::new,
VLineTo::yProperty);
private final char letter;
private final String typeName;
private final Supplier<? extends PathElement> factory;
private final Function[] propertyGetters;
<T extends PathElement> PathElementType(char letter, Class<T> type, Supplier<T> factory, Function<T, ? extends Property<?>>... propertyGetters) {
this.letter = letter;
this.typeName = type.getName();
this.factory = factory;
this.propertyGetters = propertyGetters;
}
}
private final Map<String, PathElementType> ELEMENT_TYPES_BY_TYPE;
private final Map<Character, PathElementType> ELEMENT_TYPES_BY_LETTER;
public SVGConverter() {
ELEMENT_TYPES_BY_LETTER = new HashMap<>();
ELEMENT_TYPES_BY_TYPE = new HashMap<>();
for (PathElementType et : PathElementType.values()) {
ELEMENT_TYPES_BY_LETTER.put(et.letter, et);
ELEMENT_TYPES_BY_TYPE.put(et.typeName, et);
}
}
public String pathToSvg(Path path) {
StringBuilder sb = new StringBuilder();
for (PathElement element : path.getElements()) {
PathElementType elementType = ELEMENT_TYPES_BY_TYPE.get(element.getClass().getName());
if (elementType == null) {
throw new IllegalArgumentException("Unknown PathElement type: " + element.getClass().getName());
}
// specify path element type
char c = elementType.letter;
if (element.isAbsolute()) {
c = Character.toUpperCase(c);
}
sb.append(c);
// write property values
for (Function f : elementType.propertyGetters) {
Property property = (Property) f.apply(element);
sb.append((property instanceof BooleanProperty)
// special treatment for booleans to convert true/false to 1/0
? (((BooleanProperty) property).get() ? "1" : "0")
: property.getValue().toString()).append(' ');
}
}
// trim, if necessary
int lastIndex = sb.length() - 1;
if (lastIndex >= 0 && sb.charAt(lastIndex) == ' ') {
sb.deleteCharAt(lastIndex);
}
return sb.toString();
}
private static final String NUMBER_PATTERN_STRING = "[+-]?\\d*\\.\\d*(?:[eE][+-]?\\d+)?";
private static final Pattern NUMBER_PATTERN = Pattern.compile("(?<![\\d.+-])(" + NUMBER_PATTERN_STRING + ')');
private static final Pattern SVG_PATTERN = Pattern.compile("([aAcChHlLvmMqQVzZ])((?:\\s*" + NUMBER_PATTERN_STRING + "(?:[\\s,]+" + NUMBER_PATTERN_STRING + ")*)?)");
// parses doubles from number sequence
private static double[] getNumberMatches(Matcher m, int count) {
double[] result = new double[count];
for (int i = 0; i < count; i++) {
if (!m.find()) {
throw new IllegalArgumentException("missing numbers");
}
result[i] = Double.parseDouble(m.group(1));
}
if (m.find()) {
throw new IllegalArgumentException("too many numbers");
}
return result;
}
public Path svgToPath(String svg) {
Path path = new Path();
Matcher matcher = SVG_PATTERN.matcher(svg);
while (matcher.find()) {
// find out path element type
char c = matcher.group(1).charAt(0);
PathElementType elementType = ELEMENT_TYPES_BY_LETTER.get(Character.toLowerCase(c));
if (elementType == null) {
throw new IllegalArgumentException("Unknown path type " + c);
}
PathElement element = (PathElement) elementType.factory.get();
element.setAbsolute(Character.isUpperCase(c));
// retrieve parameters
if (elementType.propertyGetters.length > 0) {
Matcher numberMatcher = NUMBER_PATTERN.matcher(matcher.group(2));
double[] numbers = getNumberMatches(numberMatcher, elementType.propertyGetters.length);
for (int i = 0; i < elementType.propertyGetters.length; i++) {
Property property = (Property) elementType.propertyGetters[i].apply(element);
property.setValue((property instanceof BooleanProperty)
? (numbers[i] == 1) // convert to boolean (true iff 1.0)
: numbers[i]);
}
}
path.getElements().add(element);
}
return path;
}
}
Note: This does not restore any kind of bindings that may have existed before converting to string of course.
I am currently working on a "code parser" parsing Valve Map Format (.vmf files) into a java readable Object.
In vmf files,
there are 2 types of objects: Classes and Properties.
classes have a name and can contain other classes and properties.
properties have a name and an unlimited number of values.
Therefore I created a VMFClass Object Class and a VMFProperty Object Class.
I created a List with self-created HierarchyObjects, containing the VMFClass/VMFProperty Object, an UUID and the parentUUID.
The VMFClass Object Contains 2 Lists one with sub-VMFClasses, one with properties.
My Problem is that I have no clue on how to achieve that a Class contains all of its subclasses, since I can't tell how much subclasses the subclasses have and so on...
Here is my Code (Github):
HierachyObject:
package net.minecraft.sourcecraftreloaded.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HierarchyObject {
private static Map<Long, Long> usedUUIDs = new HashMap<>();
private long parentUUID;
private long UUID;
private Object object;
/**
*
* #param Object
* #param parent -1 is maximum level
*/
public HierarchyObject(Object object, long parent) {
this.object = object;
this.parentUUID = parent;
while (true) {
long random = (long) (Math.random() * Long.MAX_VALUE);
if (usedUUIDs.containsKey(random)) {
this.UUID = random;
usedUUIDs.put(random, parent);
break;
}
}
}
public long getUUID() {
return UUID;
}
public long getParentUUID() {
return parentUUID;
}
public static long getParentUUIDbyUUID(long UUID) {
if (usedUUIDs.containsKey(UUID)) {
return usedUUIDs.get(UUID);
}
return -1;
}
public Object getObject() {
return object;
}
public static boolean hasChild(long UUID){
if(usedUUIDs.containsValue(UUID)){
return true;
}
if(UUID == -1){
return true;
}
return false;
}
public boolean hasChild(){
return hasChild(this.UUID);
}
public static long[] getChildUUIDs(long UUID){
if(hasChild(UUID)){
List<Long> cUUIDs = new ArrayList<>();
for(int i = 0; i < usedUUIDs.size(); i++){
for (Map.Entry<Long, Long> e : usedUUIDs.entrySet()) {
if(e.getValue().longValue() == UUID){
cUUIDs.add(e.getKey());
}
}
}
return ListUtils.toPrimitivebyList(cUUIDs);
}
return null;
}
}
VMFProperty:
package net.minecraft.sourcecraftreloaded.source;
public class VMFProperty{
private String name;
private String[] values;
public VMFProperty(String name, String... values) {
this.name = name;
this.values = values;
}
public String getName() {
return name;
}
public String[] getValues() {
return values;
}
#Override
public boolean equals(Object paramObject){
if(paramObject instanceof VMFProperty){
return ((VMFProperty)paramObject).name.equals(this.name) && ((VMFProperty)paramObject).values.equals(this.values);
}
return false;
}
}
VMFClass:
package net.minecraft.sourcecraftreloaded.source;
import java.util.List;
public class VMFClass{
private List<VMFClass> classes;
private List<VMFProperty> properties;
private String name;
public VMFClass(String name, List<VMFClass> classes, List<VMFProperty> properties) {
this.name = name;
this.classes = classes;
this.properties = properties;
}
public String getName() {
return name;
}
public List<VMFClass> getClasses() {
return classes;
}
public List<VMFProperty> getProperties() {
return properties;
}
public void add(VMFClass vmfclass) {
classes.add(vmfclass);
}
public void add(VMFProperty vmfproperty) {
properties.add(vmfproperty);
}
public void remove(VMFClass vmfclass) {
classes.remove(vmfclass);
}
public void remove(VMFProperty vmfproperty) {
properties.remove(vmfproperty);
}
#Override
public boolean equals(Object paramObject){
if(paramObject instanceof VMFClass){
return ((VMFClass)paramObject).properties.equals(this.properties) && ((VMFClass)paramObject).classes.equals(this.classes) && ((VMFClass)paramObject).name.equals(this.name);
}
return false;
}
}
VMFObject (the class executing all the code):
package net.minecraft.sourcecraftreloaded.source;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.sourcecraftreloaded.utils.HierarchyObject;
public class VMFObject {
private String rawfile = "";
private List<VMFClass> toplevelclasses;
private static final String INVALID_CHARS = "\\*,;<>|?=`´#'+~^°!§$%&()[].:-_";
public VMFObject(List<VMFClass> toplevelclasses) {
this.toplevelclasses = toplevelclasses;
}
public VMFObject() {
this(new ArrayList<VMFClass>());
}
public void write(File file) {
VMFWriter.write(file, rawfile);
}
public VMFObject read(File file) throws VMFParsingException {
this.rawfile = VMFReader.read(file);
parse();
return this;
}
public List<VMFClass> getClasses() {
return toplevelclasses;
}
private void parse() throws VMFParsingException {
evaluate();
get();
}
private void evaluate() throws VMFParsingException {
char[] textchars = rawfile.toCharArray();
int[] c = new int[]{0, 0, 0};
int line = 0;
int linepos = 0;
for (int i : textchars) {
linepos++;
if (textchars[i] == '\n') {
line++;
linepos = 0;
c[3] = 0;
if (c[3] % 2 != 0) {
throw new VMFParsingException("Invalid quotes on line" + line + ":" + linepos);
}
}
if (textchars[i] == '{') {
c[1]++;
}
if (textchars[i] == '}') {
c[2]++;
}
if (textchars[i] == '"') {
c[3]++;
if (c[1] - c[2] == 0) {
}
}
if (textchars[i] == '/' && textchars[i + 1] == '/') {
while (true) {
i++;
if (textchars[i] == '\n') {
break;
}
}
}
if (textchars[i] == '/' && textchars[i + 1] == ' ') {
throw new VMFParsingException("Invalid Character '/' on line" + line + ":" + linepos);
}
if (INVALID_CHARS.indexOf(textchars[i]) != -1) {
throw new VMFParsingException("Invalid Character '" + textchars[i] + "' on line" + line + ":" + linepos);
}
}
if (c[1] != c[2]) {
throw new VMFParsingException("Unbalanced brackets in vmf File");
}
}
public void add(VMFClass vmfclass) {
toplevelclasses.add(vmfclass);
}
private void get() throws VMFParsingException {
List<HierarchyObject> content = new ArrayList<>();
long curparent = -1;
String[] text = rawfile.split("\n");
for (int i = 0; i < text.length; i++) {
String line = text[i].trim();
if (line.startsWith("//")) {
continue;
} else {
byte quotec = 0;
char[] linechar = line.toCharArray();
boolean readp = false;
List<String> reads = new ArrayList<>();
byte creads = 0;
for (int y = 0; y < linechar.length; y++) {
if (linechar[y] == '/' && linechar[y + 1] == '/') {
break;
}
if (linechar[y] == '"') {
quotec++;
if (quotec % 2 == 0) {
readp = false;
creads++;
} else {
readp = true;
}
}
if (readp) {
reads.set(creads, reads.get(creads) + linechar[y]);
}
if (linechar[y] == '{') {
HierarchyObject object = new HierarchyObject(new VMFClass(line.substring(line.substring(0, y).lastIndexOf(' '), y).trim(), null, null), curparent);
content.add(object);
curparent = object.getUUID();
}
if (linechar[y] == '}') {
curparent = HierarchyObject.getParentUUIDbyUUID(curparent);
}
}
content.add(new HierarchyObject(new VMFProperty(reads.remove(0), reads.toArray(new String[reads.size()])), curparent));
}
}
buildObject(content);
}
private void buildObject(List<HierarchyObject> content) {
long curUUID = -1;
for(int i = 0; i < HierarchyObject.getChildUUIDs(curUUID).length; i++){
HierarchyObject.getChildUUIDs(curUUID);
}
//TODO implement
}
}
the //TODO part is where the Hierachy Object should get "converted" to the actual object.
Overview
It seems to me that your class layout is overcomplicated.
Let's try to simplify it...
What you have described with the VMF model is essentially a linked-list Tree.
Here's what the model looks like:
[.vmf file] (root)
/ \
_____/ \ _____
/ \
/ \
(VMFClass) (VMFClass)
/ \ / \
/ \ / \
/ \ / \
(VMFClass) (VMFProperties) (VMFClass) (VMFProperties)
/ \
/ \
/ \
(VMFClass) (VMFProperties)
What you need:
A Parser class (in your case, you have VMFObject, but lets call this class VMFParser).
The VMFClass and VMFProperty classes which you have are fine.
What you don't need:
The HierarchyObject class. The VMFParser can be the main controller and container for the hierarchy (e.g. the linked-list Tree model).
All the UUIDs (parent, child, etc.) These are just complicated things, but I see why you have them. You don't need them to track the hierarchy - Java will do this for us!!
VMFClass
public class VMFClass
{
// name of the class
private String name;
// reference back up to the parent
private VMFClass parentClass = null;
// all direct children go here
private List<VMFClass> children = new ArrayList<VMFClass>();
// I don't think you need a list of properties here since your VMFProperty class holds onto an array of properties
private VMFProperty properties;
// set the parent of this class
public void setParent (VMFClass parent)
{
this.parentClass = parent;
}
// get the direct children
public List<VMFClass> getChildren()
{
return this.children;
}
// rest of methods...
}
VMFParser
class VMFParser
{
private String rawfile = "";
// this is really the container for everything - think of it as the file shell
private VMFClass root = new VMFClass("root", null, null);
// construct yourself with the file
public VMFParser (String fileName)
{
this.rawfile = fileName;
}
public void parse ()
{
// all the parsing code goes here
read();
evaluate();
get();
// now at this point your hierarchy is built and stored in the
// root object in this class.
// Use the traverse method to go through it
}
private void get() throws VMFParsingException
{
// keep a reference to the current VMFClass parent
// starts out as root
VMFClass currParentClass = root;
// main parse loop
for (...)
{
// if you find a class
VMFClass currClass = new VMFClass(/* params here */);
// add this class to the parent
currParentClass.add(currClass);
// set the parent of this class
currClass.setParent(currParentClass);
// if you find a property
// parse and add all the properties to the property
VMFProperty property = new VMFProperty (/* value params here */);
// attach this property to the last VMF class that got parsed
currClass.setPoperties(property);
// If you nest deeper into classes, then the parent becomes the current class
currParentClass = currClass;
// If you go back out of a class
currParentClass = currClass.getParent();
}
}
// Traverse the hierarchy
public void traverse ()
{
traverseTree(root);
}
private void traverseTree (VMFClass root)
{
System.out.println("Class Name: " + root.getName());
// print out any properties
VMFProperty prop = root.getProperty();
if (prop != null)
{
System.out.println("Property Name: " + prop.getName());
String [] props = prop.getValues();
for (String s: props)
{
System.out.println("Value: " + s);
}
}
// get all child classes
List<VMFClass> children = root.getChildren();
for (VMFClass c: children)
{
traverseTree(c);
}
}
}
Client Code
Example
public static void main(String[] args)
{
VMFParser vmfParser = null;
try
{
vmfParser = new VMFParser("myFile.vmf");
vmfParser.parse();
// access the vmfParser for the hierarchy
vmfParser.traverse();
}
catch (VMFParsingException vpe)
{
// do something here
vpe.printStackTrace();
}
finally
{
// clean up...
}
}
If you are just looking to find all sub classes of particular class or interface , this might help you,
How can I get a list of all the implementations of an interface programmatically in Java?
I'm having this weird issue where a class from some transitive dependency keeps showing up at runtime, shadowing a newer version of the class from the (correct) first level dependency, even though I thought I made sure that I excluded the older version from all other dependencies I declare (this is in a Maven/IntelliJ setup)
More specifically, at runtime the app fails with a NoClassDefFoundError, since during class loading a wrong version of the owning class is loaded, which has a field of a type that does not exist in newer versions of the library that class is defined in. To illustrate:
// lib.jar:wrong-version
class Owner {
private SomeType f;
}
// lib.jar:new-version
class Owner {
private OtherType f;
}
At runtime, the class loader finds a reference to the symbol Owner and attempts to load the version that has SomeType, which in return does not exist anymore. This is even though I excluded wrong-version where ever I could spot it.
I also ran mvn dependency:tree to see if the old version is still being pulled in somewhere, but it's not!
In order to further debug this, I was wondering if there is a way to find out where a class loader was reading a specific class from, i.e. which file? Is that possible? Or even better, build a list of origins where a certain symbol is defined, in case it's defined more than once?
Sorry if this is vague, but the problem is rather nebulous.
The following code will search the whole classpath for a particular class. With no arguments it will dump every class it finds and then you can pipe to grep or redirect to a file. It looks inside jars...
Usage: WhichClass or WhichClass package.name (note no .class)
Apologies for the lack of comments ...
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public final class WhichClass {
private WhichClass() {
}
static Vector<String> scratchVector;
public static void main(final String[] argv) {
Vector v;
if ((argv.length == 0) || "-all".equals(argv[0])) {
v = findClass(null);
} else {
v = findClass(argv[0]);
}
for (int i = 0; i < v.size(); i++) {
System.out.println(v.elementAt(i));
}
}
static String className(final String classFile) {
return classFile.replace('/', '.').substring(0, classFile.length() - ".class".length());
}
static Vector findClass(final String className) {
if (className != null) {
scratchVector = new Vector<String>(5);
} else {
scratchVector = new Vector<String>(5000);
}
findClassInPath(className, setupBootClassPath());
findClassInPath(className, setupClassPath());
return scratchVector;
}
static void findClassInPath(final String className, final StringTokenizer path) {
while (path.hasMoreTokens()) {
String pathElement = path.nextToken();
File pathFile = new File(pathElement);
if (pathFile.isDirectory()) {
try {
if (className != null) {
String pathName = className.replace('.', System.getProperty("file.separator").charAt(0)) + ".class";
findClassInPathElement(pathName, pathElement, pathFile);
} else {
findClassInPathElement(className, pathElement, pathFile);
}
} catch (IOException e) {
e.printStackTrace();
}
} else if (pathFile.exists()) {
try {
if (className != null) {
String pathName = className.replace('.', '/') + ".class";
ZipFile zipFile = new ZipFile(pathFile);
ZipEntry zipEntry = zipFile.getEntry(pathName);
if (zipEntry != null) {
scratchVector.addElement(pathFile + "(" + zipEntry + ")");
}
} else {
ZipFile zipFile = new ZipFile(pathFile);
Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
String entry = entries.nextElement().toString();
if (entry.endsWith(".class")) {
String name = className(entry);
scratchVector.addElement(pathFile + "(" + entry + ")");
}
}
}
} catch (IOException e) {
System.err.println(e + " while working on " + pathFile);
}
}
}
}
static void findClassInPathElement(final String pathName, final String pathElement, final File pathFile)
throws IOException {
String[] list = pathFile.list();
for (int i = 0; i < list.length; i++) {
File file = new File(pathFile, list[i]);
if (file.isDirectory()) {
findClassInPathElement(pathName, pathElement, file);
} else if (file.exists() && (file.length() != 0) && list[i].endsWith(".class")) {
String classFile = file.toString().substring(pathElement.length() + 1);
String name = className(classFile);
if (pathName != null) {
if (classFile.equals(pathName)) {
scratchVector.addElement(file.toString());
}
} else {
scratchVector.addElement(file.toString());
}
}
}
}
static StringTokenizer setupBootClassPath() {
String classPath = System.getProperty("sun.boot.class.path");
String separator = System.getProperty("path.separator");
return new StringTokenizer(classPath, separator);
}
static StringTokenizer setupClassPath() {
String classPath = System.getProperty("java.class.path");
String separator = System.getProperty("path.separator");
return new StringTokenizer(classPath, separator);
}
}
If you know the fully qualified name of the class, say somelib.Owner, you can try calling the following in your code:
public void foo() {
URL url = somelib.Owner.class.getClassLoader().getResource("somelib/Owner.class");
System.out.println(url);
}
I got a bundle of code from an very old project, which they generated many redundancy methods and annotations.
Is there anyway that fast, to remove -method "doOldThing()"- from all classes in this package; remove all #AnnotationObsoluted in all class?
I know that we can use search and replace, but writing regex to delete those take a long time. I guess we could have someway to parse a Java file, then remove "doOldThings()" method, check if #AnnotationObsoluted there then remove. Any idea? Tks.
In case anyone interest, here is the code for small util that I wrote to clean up methods (by name) and import statement as well.
import com.googlecode.java2objc.main.Config;
import com.googlecode.java2objc.main.Main;
import japa.parser.ASTHelper;
import japa.parser.JavaParser;
import japa.parser.ParseException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.ImportDeclaration;
import japa.parser.ast.body.*;
import japa.parser.ast.expr.AnnotationExpr;
import japa.parser.ast.type.ClassOrInterfaceType;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* User: lent
* Date: 10/01/2014
*/
public class CleanUpAndModelRegeneration
{
public static void main(String... args) throws IOException, ParseException
{
List<File> results =
listFilesForFolder(new File("\\LocationOfYourCode"), new ArrayList<File>());
List<String> javaFiles = new ArrayList<String>();
for (File codeFile : results)
{
// creates an input stream for the file to be parsed
FileInputStream in = new FileInputStream(codeFile);
CompilationUnit cu = null;
try
{
// parse the file
cu = JavaParser.parse(in);
removeMethod(cu, "toString");
removeMethod(cu, "append");
removeMethod(cu, "appendFields");
removeMethod(cu, "fromValue");
optimizeImport(cu);
cleanUpInterfaces("ToString", cu.getTypes());
cleanUpAnnotationForEnum(cu);
// prints the resulting compilation unit to default system output
System.out.println(cu.toString());
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
finally
{
in.close();
}
FileOutputStream fileOutputStream = new FileOutputStream(codeFile, false);
fileOutputStream.write(cu.toString().getBytes());
fileOutputStream.flush();
javaFiles.add(codeFile.toString());
}
try
{
Config config = new Config();
Main main = new Main(config, javaFiles);
main.execute();
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static void cleanUpAnnotationForEnum(CompilationUnit cu)
{
for (TypeDeclaration type : cu.getTypes())
{
if (type instanceof EnumDeclaration)
{
EnumDeclaration enumDeclaration = (EnumDeclaration) type;
if (enumDeclaration.getAnnotations() != null)
{
enumDeclaration.getAnnotations().clear();
}
for (BodyDeclaration member : enumDeclaration.getMembers())
{
List<AnnotationExpr> annotations = member.getAnnotations();
if (annotations != null)
{
annotations.clear();
}
}
for (EnumConstantDeclaration member : enumDeclaration.getEntries())
{
List<AnnotationExpr> annotations = member.getAnnotations();
if (annotations != null)
{
annotations.clear();
}
}
}
}
}
private static void cleanUpInterfaces(String interfaceName, List<? extends BodyDeclaration> types)
{
for (BodyDeclaration type : types)
{
cleanUpInterface(type, interfaceName);
if (type instanceof TypeDeclaration)
{
List<BodyDeclaration> members = ((TypeDeclaration) type).getMembers();
if (members == null)
{
continue;
}
for (BodyDeclaration body : members)
{
if (body instanceof ClassOrInterfaceDeclaration)
{
cleanUpInterface(body, interfaceName);
cleanUpInterfaces(interfaceName, ((ClassOrInterfaceDeclaration) body).getMembers());
}
}
}
}
}
private static void cleanUpInterface(BodyDeclaration typeDeclaration, String interfaceName)
{
if (typeDeclaration instanceof ClassOrInterfaceDeclaration)
{
List<ClassOrInterfaceType> interfaceTypes = ((ClassOrInterfaceDeclaration) typeDeclaration).getImplements();
if (interfaceTypes == null)
{
return;
}
List<ClassOrInterfaceType> toBeRemove = new ArrayList<ClassOrInterfaceType>();
for (ClassOrInterfaceType interfaceType : interfaceTypes)
{
if (interfaceType.toString().equals(interfaceName))
{
toBeRemove.add(interfaceType);
}
}
interfaceTypes.removeAll(toBeRemove);
}
}
private static void optimizeImport(CompilationUnit cu)
{
List<ImportDeclaration> toBeRemove = new ArrayList<ImportDeclaration>();
if (cu.getImports() == null)
{
return;
}
for (ImportDeclaration importDeclaration : cu.getImports())
{
String importPackageName = importDeclaration.getName().toString();
if (importPackageName.startsWith("java.io")
|| importPackageName.startsWith("javax.xml.datatype")
|| importPackageName.startsWith("java.util")
|| importPackageName.startsWith("java.math")
|| importPackageName.startsWith("java.text"))
{
continue;
}
toBeRemove.add(importDeclaration);
}
cu.getImports().removeAll(toBeRemove);
}
public static List<File> listFilesForFolder(final File folder, List<File> result)
{
for (final File fileEntry : folder.listFiles())
{
if (fileEntry.isDirectory())
{
listFilesForFolder(fileEntry, result);
}
else
{
if (result == null)
{
result = new ArrayList<File>();
}
result.add(fileEntry);
}
}
return result;
}
private static void removeMethod(CompilationUnit cu, String methodName)
{
List<TypeDeclaration> types = cu.getTypes();
for (TypeDeclaration type : types)
{
List<BodyDeclaration> members = type.getMembers();
cleanUp(methodName, type, members);
if (type.getAnnotations() != null)
{
type.getAnnotations().clear();
}
type.setJavaDoc(null);
type.setComment(null);
}
}
private static void cleanUp(String methodName, BodyDeclaration type, List<BodyDeclaration> members)
{
List<BodyDeclaration> membersToRemove = new ArrayList<BodyDeclaration>();
for (BodyDeclaration member : members)
{
try
{
member.setJavaDoc(null);
member.setComment(null);
if (member.getAnnotations() != null)
{
member.getAnnotations().clear();
}
if (member instanceof MethodDeclaration && methodName.equals(((MethodDeclaration) member).getName()))
{
membersToRemove.add(member);
}
if (member instanceof ClassOrInterfaceDeclaration)
{
cleanUp(methodName, member, ((ClassOrInterfaceDeclaration) member).getMembers());
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
if (type instanceof TypeDeclaration)
{
((TypeDeclaration) type).getMembers().removeAll(membersToRemove);
}
else if (type instanceof ClassOrInterfaceDeclaration)
{
((ClassOrInterfaceDeclaration) type).getMembers().removeAll(membersToRemove);
}
else if (type instanceof EnumDeclaration)
{
((EnumDeclaration) type).getMembers().removeAll(membersToRemove);
}
}
private static void changeMethods(CompilationUnit cu)
{
List<TypeDeclaration> types = cu.getTypes();
for (TypeDeclaration type : types)
{
List<BodyDeclaration> members = type.getMembers();
for (BodyDeclaration member : members)
{
if (member instanceof MethodDeclaration)
{
MethodDeclaration method = (MethodDeclaration) member;
changeMethod(method);
}
}
}
}
private static void changeMethod(MethodDeclaration n)
{
// change the name of the method to upper case
n.setName(n.getName().toUpperCase());
// create the new parameter
Parameter newArg = ASTHelper.createParameter(ASTHelper.INT_TYPE, "value");
// add the parameter to the method
ASTHelper.addParameter(n, newArg);
}
}
If you erase the body of doOldThing():
doOldThing() {
}
And then inline the method, it should disappear wherever it is used. If instead you wanted to replace it with a call to doNewThing():
doOldThing() {
doNewThing();
}
and then inline.
I have a class with information about a Person that looks something like this:
public class Contact {
private String name;
private String location;
private String address;
private String email;
private String phone;
private String fax;
public String toString() {
// Something here
}
// Getters and setters.
}
I want toString() to return this.name +" - "+ this.locations + ... for all variables. I was trying to implement it using reflection as shown from this question but I can't manage to print instance variables.
What is the correct way to solve this?
From Implementing toString:
public String toString() {
StringBuilder result = new StringBuilder();
String newLine = System.getProperty("line.separator");
result.append( this.getClass().getName() );
result.append( " Object {" );
result.append(newLine);
//determine fields declared in this class only (no fields of superclass)
Field[] fields = this.getClass().getDeclaredFields();
//print field names paired with their values
for ( Field field : fields ) {
result.append(" ");
try {
result.append( field.getName() );
result.append(": ");
//requires access to private field:
result.append( field.get(this) );
} catch ( IllegalAccessException ex ) {
System.out.println(ex);
}
result.append(newLine);
}
result.append("}");
return result.toString();
}
Why do you want to reinvent the wheel when there are opensource that are already doing the job pretty nicely.
Both apache common-langs and spring support some very flexible builder pattern
For apache, here is how you do it reflectively
#Override
public String toString()
{
return ToStringBuilder.reflectionToString(this);
}
Here is how you do it if you only want to print fields that you care about.
#Override
public String toString()
{
return new ToStringBuilder(this)
.append("name", name)
.append("location", location)
.append("address", address)
.toString();
}
You can go as far as "styling" your print output with non-default ToStringStyle or even customizing it with your own style.
I didn't personally try spring ToStringCreator api, but it looks very similar.
If you are using Eclipse, this should be easy:
1.Press Alt+Shift+S
2.Choose "Generate toString()..."
Enjoy! You can have any template of toString()s.
This also works with getter/setters.
Generic toString() one-liner, using reflection and style customization:
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
...
public String toString()
{
return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
When accessing the field value, pass the instance rather than null.
Why not use code generation here? Eclipse, for example, will generate a reasoble toString implementation for you.
Another simple approach is to let Lombok generate the toString method for you.
For this:
Simply add Lombok to your project
Add the annotation #ToString to the definition of your class
Compile your class/project, and it is done
So for example in your case, your class would look like this:
#ToString
public class Contact {
private String name;
private String location;
private String address;
private String email;
private String phone;
private String fax;
// Getters and setters.
}
Example of output in this case:
Contact(name=John, location=USA, address=SF, email=foo#bar.com, phone=99999, fax=88888)
More details about how to use the annotation #ToString.
NB: You can also let Lombok generate the getters and setters for you, here is the full feature list.
If the output from ReflectionToStringBuilder.toString() is not enough readable for you, here is code that:
1) sorts field names alphabetically
2) flags non-null fields with asterisks in the beginning of the line
public static Collection<Field> getAllFields(Class<?> type) {
TreeSet<Field> fields = new TreeSet<Field>(
new Comparator<Field>() {
#Override
public int compare(Field o1, Field o2) {
int res = o1.getName().compareTo(o2.getName());
if (0 != res) {
return res;
}
res = o1.getDeclaringClass().getSimpleName().compareTo(o2.getDeclaringClass().getSimpleName());
if (0 != res) {
return res;
}
res = o1.getDeclaringClass().getName().compareTo(o2.getDeclaringClass().getName());
return res;
}
});
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}
public static void printAllFields(Object obj) {
for (Field field : getAllFields(obj.getClass())) {
field.setAccessible(true);
String name = field.getName();
Object value = null;
try {
value = field.get(obj);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.printf("%s %s.%s = %s;\n", value==null?" ":"*", field.getDeclaringClass().getSimpleName(), name, value);
}
}
test harness:
public static void main(String[] args) {
A a = new A();
a.x = 1;
B b = new B();
b.x=10;
b.y=20;
System.out.println("=======");
printAllFields(a);
System.out.println("=======");
printAllFields(b);
System.out.println("=======");
}
class A {
int x;
String z = "z";
Integer b;
}
class B extends A {
int y;
private double z = 12345.6;
public int a = 55;
}
Addition with #cletus answer, You have to fetch all model fields(upper hierarchy) and set field.setAccessible(true) to access private members. Here is the full snippet:
#Override
public String toString() {
StringBuilder result = new StringBuilder();
String newLine = System.getProperty("line.separator");
result.append(getClass().getSimpleName());
result.append( " {" );
result.append(newLine);
List<Field> fields = getAllModelFields(getClass());
for (Field field : fields) {
result.append(" ");
try {
result.append(field.getName());
result.append(": ");
field.setAccessible(true);
result.append(field.get(this));
} catch ( IllegalAccessException ex ) {
// System.err.println(ex);
}
result.append(newLine);
}
result.append("}");
result.append(newLine);
return result.toString();
}
private List<Field> getAllModelFields(Class aClass) {
List<Field> fields = new ArrayList<>();
do {
Collections.addAll(fields, aClass.getDeclaredFields());
aClass = aClass.getSuperclass();
} while (aClass != null);
return fields;
}
i will get my answer as follow:
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class findclass {
public static void main(String[] args) throws Exception, IllegalAccessException {
new findclass().findclass(new Object(), "objectName");
new findclass().findclass(1213, "int");
new findclass().findclass("ssdfs", "String");
}
public Map<String, String>map=new HashMap<String, String>();
public void findclass(Object c,String name) throws IllegalArgumentException, IllegalAccessException {
if(map.containsKey(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))){
System.out.println(c.getClass().getSimpleName()+" "+name+" = "+map.get(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))+" = "+c);
return;}
map.put(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()), name);
Class te=c.getClass();
if(te.equals(Integer.class)||te.equals(Double.class)||te.equals(Float.class)||te.equals(Boolean.class)||te.equals(Byte.class)||te.equals(Long.class)||te.equals(String.class)||te.equals(Character.class)){
System.out.println(c.getClass().getSimpleName()+" "+name+" = "+c);
return;
}
if(te.isArray()){
if(te==int[].class||te==char[].class||te==double[].class||te==float[].class||te==byte[].class||te==long[].class||te==boolean[].class){
boolean dotflag=true;
for (int i = 0; i < Array.getLength(c); i++) {
System.out.println(Array.get(c, i).getClass().getSimpleName()+" "+name+"["+i+"] = "+Array.get(c, i));
}
return;
}
Object[]arr=(Object[])c;
for (Object object : arr) {
if(object==null)
System.out.println(c.getClass().getSimpleName()+" "+name+" = null");
else {
findclass(object, name+"."+object.getClass().getSimpleName());
}
}
}
Field[] fields=c.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if(field.get(c)==null){
System.out.println(field.getType().getSimpleName()+" "+name+"."+field.getName()+" = null");
continue;
}
findclass(field.get(c),name+"."+field.getName());
}
if(te.getSuperclass()==Number.class||te.getSuperclass()==Object.class||te.getSuperclass()==null)
return;
Field[]faFields=c.getClass().getSuperclass().getDeclaredFields();
for (Field field : faFields) {
field.setAccessible(true);
if(field.get(c)==null){
System.out.println(field.getType().getSimpleName()+" "+name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName()+" = null");
continue;
}
Object check=field.get(c);
findclass(field.get(c),name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName());
}
}
public void findclass(Object c,String name,Writer writer) throws IllegalArgumentException, IllegalAccessException, IOException {
if(map.containsKey(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))){
writer.append(c.getClass().getSimpleName()+" "+name+" = "+map.get(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()))+" = "+c+"\n");
return;}
map.put(c.getClass().getName() + "#" + Integer.toHexString(c.hashCode()), name);
Class te=c.getClass();
if(te.equals(Integer.class)||te.equals(Double.class)||te.equals(Float.class)||te.equals(Boolean.class)||te.equals(Byte.class)||te.equals(Long.class)||te.equals(String.class)||te.equals(Character.class)){
writer.append(c.getClass().getSimpleName()+" "+name+" = "+c+"\n");
return;
}
if(te.isArray()){
if(te==int[].class||te==char[].class||te==double[].class||te==float[].class||te==byte[].class||te==long[].class||te==boolean[].class){
boolean dotflag=true;
for (int i = 0; i < Array.getLength(c); i++) {
writer.append(Array.get(c, i).getClass().getSimpleName()+" "+name+"["+i+"] = "+Array.get(c, i)+"\n");
}
return;
}
Object[]arr=(Object[])c;
for (Object object : arr) {
if(object==null){
writer.append(c.getClass().getSimpleName()+" "+name+" = null"+"\n");
}else {
findclass(object, name+"."+object.getClass().getSimpleName(),writer);
}
}
}
Field[] fields=c.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if(field.get(c)==null){
writer.append(field.getType().getSimpleName()+" "+name+"."+field.getName()+" = null"+"\n");
continue;
}
findclass(field.get(c),name+"."+field.getName(),writer);
}
if(te.getSuperclass()==Number.class||te.getSuperclass()==Object.class||te.getSuperclass()==null)
return;
Field[]faFields=c.getClass().getSuperclass().getDeclaredFields();
for (Field field : faFields) {
field.setAccessible(true);
if(field.get(c)==null){
writer.append(field.getType().getSimpleName()+" "+name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName()+" = null"+"\n");
continue;
}
Object check=field.get(c);
findclass(field.get(c),name+"<"+c.getClass().getSuperclass().getSimpleName()+"."+field.getName(),writer);
}
}
}