To fix a test case I need to identify whether the function is called from a particular caller function. I can't afford to add a boolean parameter because it would break the interfaces defined. How to go about this?
This is what I want to achieve. Here I can't change the parameters of operation() as it is an interface implementation.
operation()
{
if not called from performancetest() method
do expensive bookkeeping operation
...
}
You could try
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
String methodName = e.getMethodName();
Here is code that is more modern (available in Java 9+) and better performing.
private static String getCallerMethodName()
{
return StackWalker.
getInstance().
walk(stream -> stream.skip(1).findFirst().get()).
getMethodName();
}
Change skip(1) to a larger number as needed to go higher on the stack.
This performs better than Thread.currentThread().getStackTrace() because it does not walk the entire stack and allocate all of the stack frames. It only walks two frames on the stack.
This method can be adapted to return StackWalker.StackFrame that has a lot of information about the method.
Here's a function I wrote to Log the function name of the function that calls it. It runs up the stack trace until it finds a function named logIt, then displays the next name. It's a dirty hack, so don't do it unless you're using it to debug.
private static void logIt() {
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
boolean logged = false;
boolean foundMe = false;
for(int i=0; i<stacktrace.length; i++) {
StackTraceElement e = stacktrace[i];
String methodName = e.getMethodName();
if (foundMe) {
if (!methodName.startsWith("access$")) {
Log.i(TAG, String.format(Locale.US, "%s.%s", e.getClassName(), methodName));
logged = true;
break;
}
} else {
if (methodName.equals("logIt")) {
foundMe = true;
}
}
}
if (!logged)
Log.e(TAG, "unlogged call");
}
I tweaked the code that is being discussed here and customized it to get the invoking method. What the code does here is to iterate over the stack trace elements and as soon as it finds the name of the method being invoked, it gets the name of the previous method, which in turn will be the method that is invoking this method.
private String method() {
String methodName=null;
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
for (int i = 0; i < stacktrace.length; i++) {
if(stacktrace[i].getMethodName().equals("method")) {
methodName = stacktrace[i+1].getMethodName();
break;
}
}
return methodName;
}
Another sample for android usage:
//package your.package.name;
import android.util.Log;
/*
File name: MyDebugLog.java
*/
public class MyDebugLog {
private static final int index = 4; // <== Index in call stack array
private static final String methodName = "Log"; // <== Name of method for public call
private static String getCallerName() {
String caller = "NONE";
final StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
for (int i = 0; i < stacktrace.length; i++) {
Log.e("Method ", "[" + i + "]" + stacktrace[i].getMethodName());
}
if (stacktrace.length >= index){
caller = stacktrace[index].getMethodName();
}
return caller;
}
private static String getTag() {
String tag = "NONE";
final StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
for (int i = 0; i < stacktrace.length; i++) {
Log.e("Method ", "[" + i + "]" + stacktrace[i].getMethodName());
if (stacktrace[i].getMethodName().equals(methodName)) {
tag = "("+stacktrace[i + 1].getFileName() + ":" + stacktrace[i + 1].getLineNumber()+")";
return tag;
}
}
return tag;
}
public static void Log(String message){
Log.v(getTag(), getCallerName() + " " + message);
}
}
Usage:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_main);
MyDebugLog.Log("XXXXX");
}
Output:
V/(MainActivity.java:117): onCreate XXXXX
Sample of arrays:
getTag Sample of stacktace array:
Method: [0]getThreadStackTrace
Method: [1]getStackTrace
Method: [2]getTag
Method: [3]Log <== Method for external call
...
getName Sample of stacktace array:
Method: [0]getThreadStackTrace
Method: [1]getStackTrace
Method: [2]getCallerName
Method: [3]Log
Method: [4]onCreate <== Our external method
Method: [5]performCreate
...
I sometimes want to make some outputs to the logcat. So I wrote a tiny class with some testing-methods:
public class Common {
// can be used as test-flag anywhere in the app (set to false, when release the app)
public static boolean b_TEST_MODE = true;
public static void echo(String message) {
if (b_TEST_MODE) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
// subtring(25) is to cut off app-name from output
System.out.println(">>> " + stackTraceElements[3].toString().substring(25) + ": " + message);
}
}
}
now you can call it from anywhere in the app to get some infos:
String sSQLQuery = "SELECT * FROM database WHERE id=23";
Common.echo(sSQLQuery);
The logcat prints out:
>>> MainActivity.onCreate(MainActivity.java:46): SELECT * FROM dateabase WHERE id=23
I have no idea why but in my shop the develop system differs from the test and production environments shifting position in the stack. I was forced to loop through the stack to find and get the calling method from the next element in the stack trace. A little clunkier but so far has been consistently returning the desired method. I use this as part of my error handling to identify where an exception was caught.
List<String> list = new ArrayList<String>();
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
for (int i = 0; i < Thread.currentThread().getStackTrace().length; i++) {
System.out.println("Stack: "
+ i
+ " Class: "
+ elements[i].getClassName()
+ " Method: "
+ elements[i].getMethodName());
if (elements[i].getMethodName().equals("<init>")) {
list.add(elements[i + 1].getClassName());
list.add(elements[i + 1].getMethodName());
break;
} // if
} // for
Related
I am trying to utilize a .properties file to get a list of strings and transform them into an array to be added with javassist later in my program. However, I keep getting an arrayoutofboundsexception 0 with the following data in properties:
modify.spells.in.
void configure(Properties options)
{
String nameSpace;
ArrayList<String> spellList = new ArrayList<String>();
String[] tokens;
for(String key : options.stringPropertyNames()) {
if(key.startsWith("modify.spells.in.")) {
nameSpace = key.substring(17);
tokens = options.getProperty(key).split(",");
for(String token : tokens) {
if(spellList.contains(nameSpace + "." + token)) {
continue;
}
spellList.add(nameSpace + "." + token);
}
}
}
if(spellList.size() == 0) {
itemSpells = new String[]{
"Fire1", "Fire2", "Fire3", "Lit1", "Lit2", "Lit3", "Ice1", "Ice2",
"Ice3"
};
for(int i = 0; i < itemSpells.length; i++) {
itemSpells[i] = "com.ragdoll.spells." + itemSpells[i];
}
} else {
itemSpells = spellList.toArray(new String[0]);
}
The properties file string is as follows:
modify.spells.in.com.ragdoll.spells=Fire3,Lit3,Ice3
The expression to edit the existing code is as follows:
I have a HookManager to add in interceptSpells(itemSpells);
private void interceptSpells(String[] classes) throws NotFoundException, CannotCompileException
{
CtClass c;
ClassPool cp = HookManager.getInstance().getClassPool();
CtMethod m = null;
// CAST -
for(int i = 0; i < classes.length; i++) {
final int j = i;
c = cp.get(classes[i]);
m = c.getDeclaredMethods("doEffect")[0];
m.instrument(new ExprEditor() {
public void edit(MethodCall m) throws CannotCompileException {
if (m.getMethodName().equals("addSpellEffect")) {
logger.info("Intercepting (1) " + classes[j]);
m.replace(
"$_ = $proceed($$);"
+ "com.rev.spellmod.getInstance().addSpellEffectCaster($0, eff, performer); "
);
} else if(m.getMethodName().equals("improvePower")) {
logger.info("Intercepting (2) " + classes[j]);
m.replace(
"$_ = $proceed($$);"
+ "com.rev.spellmod.getInstance().replaceSpellEffectCaster($0, eff, performer); "
);
}
}
});
I feel as though I have declared the namespace and am splitting the list correctly. If I add just one spell in the .properties file, it works. As soon as I add more than one it points back to the intercept code with the arrayoutofboundsexception. If I leave it blank, it also works with the pre-determined spells, but I was hoping to be able to modify the spells on the go rather the recompiling each time.
Any thoughts or help would be appreciated!
I realized the doEffect was the issue. Some of the spells inherited doEffect from another class and so that was producing the ArrayOutOfBoundsException not the parsing of the string which was correct.
public String toString() {
for(int i = 0; i<items.length; i++) { //i++ is considered "dead code"
return "Name: " + items[i].getName() + ", Is carn? " + items[i].getVeg() + ", Pop: " + items[i].getPop();
}
return null;
}
This method should go through an array and return the name, veg and pop. Works for items[0] but not further.
Your for loop is the same as:
int i = 0:
while (i < items.length) {
return "Name: " /* etc */;
i++; // Update.
}
If you enter the loop, you start to execute the body, and then return, so you never execute the update.
If you don't enter the loop, you don't execute the update.
So you don't execute the update, hence the update is dead code.
i++ is supposed to run after first and subsequent iterations, since code is returning without any condition in the first iteration itself i++ will never be reached.
This is considered "dead code" as the return statement will end the function so instead having the for loop just print the lines and then the function ceasing it's execution following the loop.
Or if you want to use the lines being made here for something, make an array of strings out of them and return that
Right, you're probably confused about what the return statement does. I think you're trying to do the following:
public String toString() {
final StringBuilder result = new StringBuilder();
for(int i = 0; i<items.length; i++) {
result.append("Name: ").append(items[i].getName())
.append(", Is carn? ").append(items[i].getVeg())
.append(", Pop: ").append(items[i].getPop())
.append("\n");
}
return result.toString();
}
The return statement does exactly that: it returns immediately from a method and returns a result (or nothing, in case of a void method). It doesn't somehow magically append to your expected result; you'll have to do that yourself.
return an Array or a List. Also it is better to make a method toString (or print if you already have toString) in your Item class. I would do it like this.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Demo {
private Item[] items;
class Item {
private String name;
private boolean veg;
private String pop;
/* Constructor, getters and setters */
#Override
public String toString() {
return "Name: " + name + ", Is carn? " + veg + ", Pop: " + pop;
}
}
public List<String> listItemsString() {
return Stream.of(items).map(Item::toString).collect(Collectors.toList());
}
public String[] arrayItemsString() {
return Stream.of(items).map(Item::toString).toArray(String[]::new);
}
}
I am trying to add a header to the beginning of each page in Java. I am printing my console output to a file as well. All of the examples I can find are for PDF files but I need to print to a text file for later data transfer and use iText which I cannot use. Any suggestions would be awesome. Here is how I am printing so far:
PrintStream out = new PrintStream(new FileOutputStream("example.txt"));
System.setOut(out);
I'm assuming you're going to use fixed-width characters (anything else would be significantly more complicated..)
Here is a naive implementation, you can tweak it from here. I think it works pretty decently but you should test it thoroughly, also handle the case when a word is longer than a line and maybe a better exception for when a header is longer than the page:
public class PagePrinter {
private final PrintStream printer;
private final int pageWidth;
private final int pageLength;
private int currWidth = 0;
private int currLine = 0;
private int currPage = 1;
private boolean inPageHeader = false;
/**
* #param printer
* - print stream to print to
* #param pageWidth
* - in characters
* #param pageLength
* - in lines, includes the length of the header
*/
public PagePrinter(PrintStream printer, int pageWidth, int pageLength) {
this.printer = printer;
this.pageLength = pageLength;
this.pageWidth = pageWidth;
}
public void print(String str) {
// replace tabs with spaces
// may need to replace other chars that don't translate to 1 char when printed
str = str.replace("\t", " ");
// split would drop a trailing delimiter so concat extra
String[] lines = str.concat("\r\n#").split("\\r?\\n");
// print first
printWords(lines[0]);
// print rest excluding the extra
for (int i = 1; i < lines.length - 1; i++) {
// re-add delimiter (but keeping track of its affect on the page)
newline();
printWords(lines[i]);
}
}
private void printWords(String str) {
// split would drop a trailing delimiter so concat extra
String[] words = str.concat(" #").split(" ");
printWord(words[0]);
for (int i = 1; i < words.length - 1; i++) {
// re-add delimiter (but keeping track of its affect on the page)
if (currWidth < pageWidth) {
printer.print(" ");
currWidth++;
}
printWord(words[i]);
}
}
/** The smallest unit of appending to the document, */
private void printWord(String word) {
// determines when to print a header
if (currLine == 0 && !inPageHeader) {
printPageHeader();
}
int remainingSpaceOnLine = pageWidth - currWidth;
if (word.length() < remainingSpaceOnLine) {
printer.print(word);
currWidth += word.length();
} else if (word.length() < pageWidth) {
newline();
printWord(word);
} else {
// FIXME word is longer than the page width
// maybe split it with a hyphen and addWord() the parts
throw new RuntimeException("Word '" + word + "' is longer than line!");
}
}
public void newline() {
currLine++;
if (currLine >= pageLength) {
newPage();
} else {
currWidth = 0;
printer.println();
}
}
public void newPage() {
if (inPageHeader) {
throw new RuntimeException("Page header is longer than the page!!!");
}
currWidth = 0;
currLine = 0;
currPage++;
printer.println();
}
private void printPageHeader() {
inPageHeader = true;
myPageHeader();
inPageHeader = false;
}
protected void myPageHeader() {
print("----- Page " + currPage + " -----\n");
}
public static void main(String[] args) {
PagePrinter test = new PagePrinter(System.out, 40, 10);
test.print("\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because..."
+ "\n\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because..."
+ "\n\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because.."
+ "\n\tThis is the song that never ends. Yes, it goes on and on my friend. "
+ "Some people started singing it not knowing what it was "
+ "and they'll continue singing it forever just because...");
test.newPage();
test.print("This is a new page!");
test.newline();
test.print("This is a newline even though part would've fit on the previous!");
}
}
Array of objects and static/non-static methods.
class Foititis //Dimiourgeia twn antikeimenwn typou foititis
{
private String onoma, epitheto;
private short AriMit, EtosEis;
public Foititis (String on, String ep, short AM, short EE)
{
onoma = on;
epitheto = ep;
AriMit = AM;
EtosEis = EE;
}
public String getEpwnymo() //Prosbash sto epitheto apo tis alles klaseis
{
return epitheto;
}
public String toString()
{
String emf;
emf = "--------------------" + "\n";
emf = "Onoma" + onoma + "\n";
emf = "Epwnymo" + epitheto + "\n";
emf = "Arithmos Mitrwoy" + AriMit + "\n";
emf = "Etos Eisagwnis" + EtosEis + "\n";
emf = "--------------------";
return emf;
}
}
class MyUtils01 //Anazitisi Me Epwnymo, Seiriaki Anazitisi
{
public static int AnazitisiMeEpwnymo(Foititis[] a, String key)
{
boolean flag = false;
int j = 0;
return -1;
while ( !flag && j < a.length)
{
if (a[j].getEpwnymo.equals(key))
{
flag = true;
return j;
}
j += 1;
}
}
}
At this program, I have an array (named pinakas) and sent to the method "AnazitisiMeEpwnymo" of the class "MyUtils01", and renamed "a" (in order to be used). The array is an array of objects of the type "Foititis". So I want to have access from the class "MyUtils01" at the attribute "epitheto", which is private, that's why I tried to use a get method, but I get an error message.
Thank you in advance, I know that my thread may be covered somehow at another post, but none (from those that I found) where not using array of objects, and did not try to call the method like above.
You're missing some parentheses
a[j].getEpwnymo().equals(key)
^^
not
a[j].getEpwnymo.equals(key)
How can I get a class reference/TypeElement of a specific identifier at compile time in Java?
Say I have the following source file, and I want to get a reference to the Pixel class so I can get a list of its member fields.
package com.foo.bar;
class Test {
class Pixel {
int x,y,r,g,b;
}
Pixel saturate(Pixel p, int value) {...}
}
The Pixel class definition could be nested inside the Test class, or included from a different package where the source is not available.
I am using the javax.tools API to compile the source files, and I define visitor methods so I can view the arguments to each function. The arguments of a function can be iterated using VariableTree var : node.getParameters(), but the type information from var.getType() only triggers visitIdentifier for class names. This identifier is only the simple name Pixel, not the fully-qualified com.foo.bar.Pixel.
I need a way to reverse this identifier into either Pixel.class or into the TypeElement for the definition of the Pixel class, or into the fully-qualified com.foo.bar.Pixel string so I can then use a ClassLoader on it.
A crude way would be to record all class definitions and then try to do compile-time type lookup, but this wouldn't work for externally-defined classes.
As far as I remember var.getType().toString() returns you the fully qualified class name. Unfortunately I cannot check it right now, but try it yourself.
Yes, I know, it is a very bad style to use toString() for something except logging but it seems they have not given us other choice.
I ended up creating my own class lookup tool. For those that are interested, I'll include it here.
Call this with the path name of every source file to be included in the search:
public void populateClassDefinitions(String path) {
Iterable<? extends JavaFileObject> files = fileManager.getJavaFileObjects(path);
CompilationTask task =
compiler.getTask(null, fileManager, diagnosticsCollector, null, null, files);
final JavacTask javacTask = (JavacTask) task;
parseResult = null;
try {
parseResult = javacTask.parse();
} catch (IOException e) {
e.printStackTrace();
return;
}
for (CompilationUnitTree tree : parseResult) {
tree.accept(new TreeScanner<Void, Void>() {
#Override
public Void visitCompilationUnit(CompilationUnitTree node, Void p) {
currentPackage = "";
ExpressionTree packageName = node.getPackageName();
if (packageName != null) {
String packageNameString = String.valueOf(packageName);
if (packageNameString.length() > 0) {
currentPackage = packageNameString;
}
}
TreeScanner<Void, String> visitor = new TreeScanner<Void, String>() {
#Override
public Void visitClass(ClassTree node, String packagePrefix) {
if (classDefinitions.get(currentPackage) == null) {
classDefinitions.put(currentPackage, new HashMap<String, ClassTree>());
}
classDefinitions.get(currentPackage).put(packagePrefix + node.getSimpleName(), node);
return super.visitClass(node, packagePrefix + node.getSimpleName() + ".");
}
};
for (Tree decls : node.getTypeDecls()) {
decls.accept(visitor, "");
}
return super.visitCompilationUnit(node, p);
}
}, null);
}
}
Call this to search for classes.
/**
* Lookup the definition of a class.
*
* Lookup order: 1. Search in the current file: within the current class scope upwards to the
* root. 2. Search laterally across files with the same package value for implicitly included
* classes. 3. Check all import statements.
*
* #param pack
* Current package ex "edu.illinois.crhc"
* #param scope
* Current scope ex "Test.InnerClass"
* #param identifier
* The partial class name to search for
* #return ClassTree the definition of this class if found
*/
ClassLookup lookupClass(CompilationUnitTree packTree, String scope, String identifier) {
dumpClassTable();
String pack = packTree.getPackageName().toString();
System.out.println("Looking for class " + pack + " - " + scope + " - " + identifier);
// Search nested scope and within same package
HashMap<String, ClassTree> packClasses = classDefinitions.get(pack);
if (packClasses != null) {
String[] scopeWalk = scope.split("\\.");
for (int i = scopeWalk.length; i >= 0; i--) {
StringBuilder scopeTest = new StringBuilder();
for (int j = 0; j < i; j++) {
scopeTest.append(scopeWalk[j] + ".");
}
scopeTest.append(identifier);
System.out.println("Testing scope " + pack + " - " + scopeTest.toString());
if (packClasses.containsKey(scopeTest.toString())) {
return new ClassLookup(packClasses.get(scopeTest.toString()), pack.replace(".", "/")
+ "/" + scopeTest.toString().replace(".", "$"));
}
}
}
/*
* Check if fully-qualified identifier (foo.bar.Widget) is used. This needs to search all
* combinations of package and class nesting.
*/
StringBuilder packTest = new StringBuilder();
String[] qualifiedName = identifier.split("\\.");
for (int i = 0; i < qualifiedName.length - 1; i++) {
packTest.append(qualifiedName[i]);
if (i != qualifiedName.length - 2) {
packTest.append(".");
}
}
String clazz = qualifiedName[qualifiedName.length - 1];
System.out.println("Testing absolute identifier: " + packTest.toString() + " " + clazz);
if (classDefinitions.containsKey(packTest.toString())) {
HashMap<String, ClassTree> foundPack = classDefinitions.get(packTest.toString());
if (foundPack.containsKey(clazz)) {
return new ClassLookup(foundPack.get(clazz), packTest.toString().replace(".", "/") + "/"
+ clazz.replace(".", "$"));
}
}
/*
* Search import statements. Last identifier segment must be class name. Search all of the
* packages for the identifier by splitting off the class name. a.b.c.Tree Tree.Branch
* Tree.Branch.Leaf
*/
for (ImportTree imp : currentPackTree.getImports()) {
pack = imp.getQualifiedIdentifier().toString();
System.out.println(pack);
String[] importName = pack.split("\\.");
// Split off class name.
// TODO: (edge case) no package
StringBuilder importTest = new StringBuilder();
for (int i = 0; i < importName.length - 1; i++) {
importTest.append(importName[i]);
if (i != importName.length - 2) {
importTest.append(".");
}
}
// See if the last import segment is * or matches the first segment of the identifier.
System.out.println("Testing globally " + importTest.toString() + " - " + identifier);
if (classDefinitions.containsKey(importTest.toString())) {
HashMap<String, ClassTree> foundPack = classDefinitions.get(importTest.toString());
String[] identifierParts = identifier.split(".");
String importClass = importName[importName.length-1];
if (importClass.equals("*") || identifierParts[0].equals(importClass)) {
if (foundPack.containsKey(identifier)) {
return new ClassLookup(foundPack.get(identifier), importTest.toString().replace(".", "/")
+ "/" + identifier.replace(".", "$"));
}
}
}
}
return null;
}