I am new to eclipse plugin development and I am trying to convert a IMethod to a string representation of the full method name. I.E.
my.full.package.ClassName.methodName(int param, String string)
so far I have had to hand roll my own solution. Is there a better way?
private static String getMethodFullName(IMethod iMethod)
{
String packageString = "[Default Package]";
try {
IPackageDeclaration[] declarations = iMethod.getCompilationUnit().getPackageDeclarations();
if(declarations.length > 0)
{
packageString = declarations[0].getElementName();
}
} catch (JavaModelException e) {
}
String classString = iMethod.getCompilationUnit().getElementName();
classString = classString.replaceAll(".java", "");
String methodString = iMethod.getElementName() + "(";
for (String type : iMethod.getParameterTypes()) {
methodString += type + ",";
}
methodString += ")";
return packageString + "." + classString + "." + methodString;
}
You can get the Fully qualified name for the type using
method.getDeclaringType().getFullyQualifiedName();
This is probably easier than accessing the package from the compilation unit. The rest of you function looks correct.
One small point: you should use StringBuilder to build up the string instead of adding to a standard String. Strings are immutable so addition creates loads of unrecesary temparary objects.
private static String getMethodFullName(IMethod iMethod)
{
StringBuilder name = new StringBuilder();
name.append(iMethod.getDeclaringType().getFullyQualifiedName());
name.append(".");
name.append(iMethod.getElementName());
name.append("(");
String comma = "";
for (String type : iMethod.getParameterTypes()) {
name.append(comma);
comma = ", ";
name.append(type);
}
name.append(")");
return name.toString();
}
Thanks to iain and some more research I have come up with this solution. It seems like something like this should be built into the JDT....
import org.eclipse.jdt.core.Signature;
private static String getMethodFullName(IMethod iMethod)
{
StringBuilder name = new StringBuilder();
name.append(iMethod.getDeclaringType().getFullyQualifiedName());
name.append(".");
name.append(iMethod.getElementName());
name.append("(");
String comma = "";
String[] parameterTypes = iMethod.getParameterTypes();
try {
String[] parameterNames = iMethod.getParameterNames();
for (int i=0; i<iMethod.getParameterTypes().length; ++i) {
name.append(comma);
name.append(Signature.toString(parameterTypes[i]));
name.append(" ");
name.append(parameterNames[i]);
comma = ", ";
}
} catch (JavaModelException e) {
}
name.append(")");
return name.toString();
}
I am not sure it would take into account all cases (method within an internal class, an anonymous class, with generic parameters...)
When it comes to methods signatures, the classes to look into are:
org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedMethodsOperation
org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2
You need to get the jdt.core.dom.IMethodBinding, from which you can extract all what you need.
If you have a MethodInvocation, you can:
//MethodInvocation node
ITypeBinding type = node.getExpression().resolveTypeBinding();
IMethodBinding method=node.resolveMethodBinding();
Related
I am trying to add an object inside an object using recursion. My object contains an arrayList and I am trying to add my objects to this arrayList. But instead of adding a new object, my objects are being replaced.
My code which is doing this: This is where the logic of adding an object is being done. But it is being replaced instead.
private ArrayList<SubChapters> recursiveSubChapters(ReportingTree tree, LinkedHashMap<String, HashMap<String, String>> linkedHashMap, Boolean isSubTree){
SubChapters subChapters = new Subchapters();
ArrayList<SubChapters> alchildUnits = new ArrayList<SubChapters>();
final String chapterId = linkedHashMap.get(tree.getUnitID()).get("unit_num");
final String chapterName= linkedHashMap.get(tree.getUnitID()).get("unit_name");
if (!isSubTree) {
subChapters.set(chapterId);
subChapters.setTreeName(chapterName);
}
final ArrayList<ReportingTree> branches = tree.getBranches();
if (branches != null) {
subChapters.hasSubUnits(true);
for (ReportingTree subTree: branches) {
subChapters.setSubChapters(recursiveSubChapters(subTree, linkedHashMap, false));
//This is where the logic of adding an object is being done. But it is being replaced instead.
}
alchildUnits.add(subChapters);
}
return alchildUnits;
}
My guess is that I am messing somewhere in the loop here but I am not able to figure out where I am messing up. Thanks in advance for any suggestions or help.
My subChapters class:
public String subChapterID;
public String subChapterName;
public boolean isSubTree= false;
public ArrayList<SubChapters> subChapters;
and getters and setters.
I have coded the same solution to return a string and see the order on a jsp. It works just fine. I am not able to apply the same to my issue here.
private String recursive(ReportingTree tree, LinkedHashMap<String, HashMap<String, String>> listUnitInfo, boolean isTop) {
final String unitID = tree.getUnitID();
final HashMap<String, String> unit = listUnitInfo.get(unitID);
String output = "";
if (!isTop) {
output += "<li>" + unit.get("unit_num") + "/" + unit.get("unit_name") + "";
}
final ArrayList<ReportingTree> branches = tree.getBranches();
if (branches != null) {
if (isTop) {
output += "<li>" + unit.get("unit_num") + "/" + unit.get("unit_name") + "";
}
output += "<ul>\n";
for (ReportingTree subTree : branches) {
output += recursive(subTree, listUnitInfo, false);
}
output += "</ul>";
} else {
if (isTop) {
output += "<li>No units match your criteria.";
}
}
output += "</li>\n";
return output;
}
What you're doing is subChapters.setSubChapters, what I think you're trying to do is
subChapters.addSubChapters.
The reason why it works with the strings is because you're using += to add
the new string to the old string. Doing setSubChapters would be the same as using = with the strings.
addSubChapters would be a method that should add something to an ArrayList variable inside your subChapters class.
My code is to add RSS feeds to a list - and the code originally was only to pull one feed from the first position in a list, and add this object to another list.
This was the original code:
public static List<Feed> getFeedsFromXml(String xml) {
Pattern feedPattern = Pattern.compile("<feed>\\s*<name>\\s*([^<]*)</name>\\s*<uri>\\s*([^<]*)</uri>\\s*</feed>");
Matcher feedMatch = feedPattern.matcher(xml);
while (feedMatch.find()) {
String feedName = feedMatch.group(1);
String feedURI = feedMatch.group(2);
feeds.add(new Feed(feedName, feedURI));
}
return feeds;
}
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addXmlFeed() throws IOException
{
int i = 0;
String stringXml = "<feed><name>SMH Top Headlines</name><uri>http://feeds.smh.com.au/rssheadlines/top.xml</uri></feed><feed><name>UTS Library News</name>";
getFeedsFromXml(stringXml);
Feed f = (Feed) feeds.get(0);
feedList.add(f);
String handler = "You have successfully added: \n";
String xmlStringReply = "" + f + "\n";
feedList.save(feedFile);
return handler + xmlStringReply;
}
Everything was going well, and then I decided to implement a for loop for handling the adding of more than one feed to the list, and I tried the following (only the code for the second method in question):
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addXmlFeed() throws IOException
{
int i = 0;
String stringXml = "<feed><name>SMH Top Headlines</name><uri>http://feeds.smh.com.au/rssheadlines/top.xml</uri></feed><feed><name>UTS Library News</name>";
getFeedsFromXml(stringXml);
for (Feed feed: feeds)
{
Feed f = (Feed) feeds.get(i++);
feedList.add(f);
String handler = "You have successfully added: \n";
String xmlStringReply = "" + f + "\n";
}
feedList.save(feedFile);
return handler + xmlStringReply;
}
Now I'm sure this is a basic problem, but now in the line:
return handler + xmlStringReply;
handler and xmlStringReply cannot be resolved to a variable as they are within the FOR LOOP.
Is there any easy way around this?
The scope of those 2 variables is limited to the for loop. To access them outside the loop, you need to increase their scope by declaring them before the loop:
String handler = "";
String xmlStringReply = "";
for (Feed f: feeds) {
feedList.add(f);
handler = "You have successfully added: \n";
xmlStringReply = "" + f + "\n";
}
feedList.save(feedFile);
return handler + xmlStringReply;
Also, your current code overwrites the value of your strings at each loop, whereas you probably meant to concatenate the values. In that case, you could use a StringBuilder instead of string concatenation:
StringBuilder xmlStringReply = new StringBuilder("You have successfully added: \n");
for (Feed f: feeds) {
feedList.add(f);
xmlStringReply.append(f + "\n");
}
feedList.save(feedFile);
return xmlStringReply.toString();
The question you need to answer is "what do I want to return if I add several feeds ?".
Maybe you'd like to return "You have successfully added : feed1 feed2 feed3\n"
In that case, the code is :
StringBuilder response = new StringBuilder( "You have successfully added: ");
for (Feed feed: feeds)
{
feedList.add(feed);
response.append(f.toString()).append(" ");
}
feedList.save(feedFile);
return response.toString();
By the way, your feedand fvariables are just the same and redondant !
Don't write :
int i = 0;
for (Feed feed: feeds)
{
Feed f = (Feed) feeds.get(i++);
feedList.add(f);
}
but
for (Feed feed: feeds)
{
feedList.add(feed);
}
You need to accumulate the result into a variable. I am using StringBuilder because it makes string concatenation efficient.
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addXmlFeed() throws IOException
{
String stringXml = "<feed><name>SMH Top Headlines</name><uri>http://feeds.smh.com.au/rssheadlines/top.xml</uri></feed><feed><name>UTS Library News</name>";
getFeedsFromXml(stringXml);
StringBuilder replyBuilder = new StringBuilder("You have successfully added: \n");
for (Feed feed : feeds)
{
feedList.add(feed);
String xmlStringReply = feed + "\n";
reployBuilder.append(xmlStringReply);
}
feedList.save(feedFile);
return replyBuilder.toString();
}
Because, now they became out of scope.
Beside the original error -- you can easily fix that using other suggestions, I would like to suggest that you should not make feeds as instance variable. I can see your method getFeedsFromXml() is returning the list. So, I think it would have been better if you define that variable inside that method. And then, call the method like,
List<Feed> feeds = getFeedsFromXml(stringXml);
Or in case, this doesn't give you the desired behaviour, then you should rename the method to something, loadFeedsFromXml(). Making that as instance variable may result in threading issues.
Now, trying to improve on your looping,
StringBuilder xmlStringReply = new StringBuilder("You have successfully added: \n");
for (Feed feed: feeds) {
feedList.add(feed);
xmlStringReply.append(f + "\n");
}
feedList.save(feedFile);
return xmlStringReply.toString();
Moreover, I found that your feedList is also a instance variable. And this again can cause threading issues, as it doesn't sound immutable or stateless. Synchronising the methods will give you performance issues. See if you can make it local to this method. A rule of thumb is to keep variable scope as narrow as possible.
A good rule of thumb is to view scope like this:
{ //This is a constructor
int i;
} // This is a deconstructor
anything that is created / instantiated between the curlies only lives inside the curlies. Whenever your working with variables and loops:
for(int i = 0; i < 10; i++){
//some code here
} // after this curly i is no longer in scope or accessible.
I'm wanting to set a final variable after starting the constant. This works if the variable is not final, but that kind of defeats the purpose.
Can I do something similar to this?
public static final String ASDF = null;
{
ASDF = "asdf";
}
My situation:
public static JSONArray CATEGORIES = null;
{
String str = "";
str += "\"" + FIRST_CATEGORY + "\"";
try {
CATEGORIES = new JSONArray("[" + str + "]");
} catch (JSONException e) {
e.printStackTrace();
}
};
You can use a static initialization block - at least, you can in regular Java SE (I'm assuing android can too):
public static final JSONArray CATEGORIES;
static {
String str = "\"" + FIRST_CATEGORY + "\"";
try {
CATEGORIES = new JSONArray("[" + str + "]");
} catch (JSONException e) {
e.printStackTrace();
}
}
Note that you are not first intializing CATEGORIES to null, it is left uninitialized until the static block happens.
Although, you're probably going to want to hard-fail if the intialization generates an exception, because otherwise you'll have an improperly initialized variable (serious problems possible).
And, unless the JSONArray class is immutable, declaring the instance final is ~sorta pointless.
Figured it out. I'm not exactly sure what's going on here, but it works.
public static final JSONArray CATEGORIES = new JSONArray() {
{
put(FIRST_CATEGORY);
// etc eg. put(SECOND_CATEGORY);
}
};
you can choose to not include "= null" on your variable declaration. just make sure that you assign a value to your variable once - be it inside an if-else, a loop, or whatever - the compiler will detect if you're breaking this rule, and won't let your program compile.
public static JSONArray CATEGORIES = null;
{
String str;
str += "\"" + FIRST_CATEGORY + "\"";
try {
CATEGORIES = new JSONArray("[" + str + "]");
} catch (JSONException e) {
e.printStackTrace();
}
};
I am using the String split method and I want to have the last element.
The size of the Array can change.
Example:
String one = "Düsseldorf - Zentrum - Günnewig Uebachs"
String two = "Düsseldorf - Madison"
I want to split the above Strings and get the last item:
lastone = one.split("-")[here the last item] // <- how?
lasttwo = two.split("-")[here the last item] // <- how?
I don't know the sizes of the arrays at runtime :(
You could use lastIndexOf() method on String
String last = string.substring(string.lastIndexOf('-') + 1);
Save the array in a local variable and use the array's length field to find its length. Subtract one to account for it being 0-based:
String[] bits = one.split("-");
String lastOne = bits[bits.length-1];
Caveat emptor: if the original string is composed of only the separator, for example "-" or "---", bits.length will be 0 and this will throw an ArrayIndexOutOfBoundsException. Example: https://onlinegdb.com/r1M-TJkZ8
You can use the StringUtils class in Apache Commons:
StringUtils.substringAfterLast(one, "-");
using a simple, yet generic, helper method like this:
public static <T> T last(T[] array) {
return array[array.length - 1];
}
you can rewrite:
lastone = one.split("-")[..];
as:
lastone = last(one.split("-"));
String str = "www.anywebsite.com/folder/subfolder/directory";
int index = str.lastIndexOf('/');
String lastString = str.substring(index +1);
Now lastString has the value "directory"
Gathered all possible ways together!!
By using lastIndexOf() & substring() methods of Java.lang.String
// int firstIndex = str.indexOf( separator );
int lastIndexOf = str.lastIndexOf( separator );
String begningPortion = str.substring( 0, lastIndexOf );
String endPortion = str.substring( lastIndexOf + 1 );
System.out.println("First Portion : " + begningPortion );
System.out.println("Last Portion : " + endPortion );
split()Java SE 1.4. Splits the provided text into an array.
String[] split = str.split( Pattern.quote( separator ) );
String lastOne = split[split.length-1];
System.out.println("Split Array : "+ lastOne);
How to split String before first comma?
Java 8 sequential ordered stream from an array.
String firstItem = Stream.of( split )
.reduce( (first,last) -> first ).get();
String lastItem = Stream.of( split )
.reduce( (first,last) -> last ).get();
System.out.println("First Item : "+ firstItem);
System.out.println("Last Item : "+ lastItem);
Apache Commons Langjar « org.apache.commons.lang3.StringUtils
String afterLast = StringUtils.substringAfterLast(str, separator);
System.out.println("StringUtils AfterLast : "+ afterLast);
String beforeLast = StringUtils.substringBeforeLast(str, separator);
System.out.println("StringUtils BeforeLast : "+ beforeLast);
String open = "[", close = "]";
String[] groups = StringUtils.substringsBetween("Yash[777]Sam[7]", open, close);
System.out.println("String that is nested in between two Strings "+ groups[0]);
Guava: Google Core Libraries for Java. « com.google.common.base.Splitter
Splitter splitter = Splitter.on( separator ).trimResults();
Iterable<String> iterable = splitter.split( str );
String first_Iterable = Iterables.getFirst(iterable, "");
String last_Iterable = Iterables.getLast( iterable );
System.out.println(" Guava FirstElement : "+ first_Iterable);
System.out.println(" Guava LastElement : "+ last_Iterable);
Scripting for the Java Platform « Run Javascript on the JVM with Rhino/Nashorn
Rhino « Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users. It is embedded in J2SE 6 as the default Java scripting engine.
Nashorn is a JavaScript engine developed in the Java programming language by Oracle. It is based on the Da Vinci Machine and has been released with Java 8.
Java Scripting Programmer's Guide
public class SplitOperations {
public static void main(String[] args) {
String str = "my.file.png.jpeg", separator = ".";
javascript_Split(str, separator);
}
public static void javascript_Split( String str, String separator ) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// Script Variables « expose java objects as variable to script.
engine.put("strJS", str);
// JavaScript code from file
File file = new File("E:/StringSplit.js");
// expose File object as variable to script
engine.put("file", file);
try {
engine.eval("print('Script Variables « expose java objects as variable to script.', strJS)");
// javax.script.Invocable is an optional interface.
Invocable inv = (Invocable) engine;
// JavaScript code in a String
String functions = "function functionName( functionParam ) { print('Hello, ' + functionParam); }";
engine.eval(functions);
// invoke the global function named "functionName"
inv.invokeFunction("functionName", "function Param value!!" );
// evaluate a script string. The script accesses "file" variable and calls method on it
engine.eval("print(file.getAbsolutePath())");
// evaluate JavaScript code from given file - specified by first argument
engine.eval( new java.io.FileReader( file ) );
String[] typedArray = (String[]) inv.invokeFunction("splitasJavaArray", str );
System.out.println("File : Function returns an array : "+ typedArray[1] );
ScriptObjectMirror scriptObject = (ScriptObjectMirror) inv.invokeFunction("splitasJavaScriptArray", str, separator );
System.out.println("File : Function return script obj : "+ convert( scriptObject ) );
Object eval = engine.eval("(function() {return ['a', 'b'];})()");
Object result = convert(eval);
System.out.println("Result: {}"+ result);
// JavaScript code in a String. This code defines a script object 'obj' with one method called 'hello'.
String objectFunction = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
engine.eval(objectFunction);
// get script object on which we want to call the method
Object object = engine.get("obj");
inv.invokeMethod(object, "hello", "Yash !!" );
Object fileObjectFunction = engine.get("objfile");
inv.invokeMethod(fileObjectFunction, "hello", "Yashwanth !!" );
} catch (ScriptException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static Object convert(final Object obj) {
System.out.println("\tJAVASCRIPT OBJECT: {}"+ obj.getClass());
if (obj instanceof Bindings) {
try {
final Class<?> cls = Class.forName("jdk.nashorn.api.scripting.ScriptObjectMirror");
System.out.println("\tNashorn detected");
if (cls.isAssignableFrom(obj.getClass())) {
final Method isArray = cls.getMethod("isArray");
final Object result = isArray.invoke(obj);
if (result != null && result.equals(true)) {
final Method values = cls.getMethod("values");
final Object vals = values.invoke(obj);
System.err.println( vals );
if (vals instanceof Collection<?>) {
final Collection<?> coll = (Collection<?>) vals;
Object[] array = coll.toArray(new Object[0]);
return array;
}
}
}
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
}
}
if (obj instanceof List<?>) {
final List<?> list = (List<?>) obj;
Object[] array = list.toArray(new Object[0]);
return array;
}
return obj;
}
}
JavaScript file « StringSplit.js
// var str = 'angular.1.5.6.js', separator = ".";
function splitasJavaArray( str ) {
var result = str.replace(/\.([^.]+)$/, ':$1').split(':');
print('Regex Split : ', result);
var JavaArray = Java.to(result, "java.lang.String[]");
return JavaArray;
// return result;
}
function splitasJavaScriptArray( str, separator) {
var arr = str.split( separator ); // Split the string using dot as separator
var lastVal = arr.pop(); // remove from the end
var firstVal = arr.shift(); // remove from the front
var middleVal = arr.join( separator ); // Re-join the remaining substrings
var mainArr = new Array();
mainArr.push( firstVal ); // add to the end
mainArr.push( middleVal );
mainArr.push( lastVal );
return mainArr;
}
var objfile = new Object();
objfile.hello = function(name) { print('File : Hello, ' + name); }
JavaScript Array constructor or array literal.
With Guava:
final Splitter splitter = Splitter.on("-").trimResults();
assertEquals("Günnewig Uebachs", Iterables.getLast(splitter.split(one)));
assertEquals("Madison", Iterables.getLast(splitter.split(two)));
Splitter, Iterables
Since he was asking to do it all in the same line using split so i suggest this:
lastone = one.split("-")[(one.split("-")).length -1]
I always avoid defining new variables as far as I can, and I find it a very good practice
You mean you don't know the sizes of the arrays at compile-time? At run-time they could be found by the value of lastone.length and lastwo.length .
Also you can use java.util.ArrayDeque
String last = new ArrayDeque<>(Arrays.asList("1-2".split("-"))).getLast();
In java 8
String lastItem = Stream.of(str.split("-")).reduce((first,last)->last).get();
I guess you want to do this in i line. It is possible (a bit of juggling though =^)
new StringBuilder(new StringBuilder("Düsseldorf - Zentrum - Günnewig Uebachs").reverse().toString().split(" - ")[0]).reverse()
tadaa, one line -> the result you want (if you split on " - " (space minus space) instead of only "-" (minus) you will loose the annoying space before the partition too =^) so "Günnewig Uebachs" instead of " Günnewig Uebachs" (with a space as first character)
Nice extra -> no need for extra JAR files in the lib folder so you can keep your application light weight.
I was writing a toString() for a class in Java the other day by manually writing out each element of the class to a String and it occurred to me that using reflection it might be possible to create a generic toString() method that could work on ALL classes. I.E. it would figure out the field names and values and send them out to a String.
Getting the field names is fairly simple, here is what a co-worker came up with:
public static List initFieldArray(String className) throws ClassNotFoundException {
Class c = Class.forName(className);
Field field[] = c.getFields();
List<String> classFields = new ArrayList(field.length);
for (int i = 0; i < field.length; i++) {
String cf = field[i].toString();
classFields.add(cf.substring(cf.lastIndexOf(".") + 1));
}
return classFields;
}
Using a factory I could reduce the performance overhead by storing the fields once, the first time the toString() is called. However finding the values could be a lot more expensive.
Due to the performance of reflection this may be more hypothetical then practical. But I am interested in the idea of reflection and how I can use it to improve my everyday programming.
Apache commons-lang ReflectionToStringBuilder does this for you.
import org.apache.commons.lang3.builder.ReflectionToStringBuilder
// your code goes here
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
Another option, if you are ok with JSON, is Google's GSON library.
public String toString() {
return new GsonBuilder().setPrettyPrinting().create().toJson(this);
}
It's going to do the reflection for you. This produces a nice, easy to read JSON file. Easy-to-read being relative, non tech folks might find the JSON intimidating.
You could make the GSONBuilder a member variable too, if you don't want to new it up every time.
If you have data that can't be printed (like a stream) or data you just don't want to print, you can just add #Expose tags to the attributes you want to print and then use the following line.
new GsonBuilder()
.setPrettyPrinting()
.excludeFieldsWithoutExposeAnnotation()
.create()
.toJson(this);
W/reflection, as I hadn't been aware of the apache library:
(be aware that if you do this you'll probably need to deal with subobjects and make sure they print properly - in particular, arrays won't show you anything useful)
#Override
public String toString()
{
StringBuilder b = new StringBuilder("[");
for (Field f : getClass().getFields())
{
if (!isStaticField(f))
{
try
{
b.append(f.getName() + "=" + f.get(this) + " ");
} catch (IllegalAccessException e)
{
// pass, don't print
}
}
}
b.append(']');
return b.toString();
}
private boolean isStaticField(Field f)
{
return Modifier.isStatic(f.getModifiers());
}
If you're using Eclipse, you may also have a look at JUtils toString generator, which does it statically (generating the method in your source code).
You can use already implemented libraries, as ReflectionToStringBuilder from Apache commons-lang. As was mentioned.
Or write smt similar by yourself with reflection API.
Here is some example:
class UniversalAnalyzer {
private ArrayList<Object> visited = new ArrayList<Object>();
/**
* Converts an object to a string representation that lists all fields.
* #param obj an object
* #return a string with the object's class name and all field names and
* values
*/
public String toString(Object obj) {
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String) obj;
if (cl.isArray()) {
String r = cl.getComponentType() + "[]{";
for (int i = 0; i < Array.getLength(obj); i++) {
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
// inspect the fields of this class and all superclasses
do {
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// get the names and values of all fields
for (Field f : fields) {
if (!Modifier.isStatic(f.getModifiers())) {
if (!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try {
Class t = f.getType();
Object val = f.get(obj);
if (t.isPrimitive()) r += val;
else r += toString(val);
} catch (Exception e) {
e.printStackTrace();
}
}
}
r += "]";
cl = cl.getSuperclass();
} while (cl != null);
return r;
}
}
Not reflection, but I had a look at generating the toString method (along with equals/hashCode) as a post-compilation step using bytecode manipulation. Results were mixed.
Here is the Netbeans equivalent to Olivier's answer; smart-codegen plugin for Netbeans.