Getting the name of a method parameter - java

In Java 6, imagine I have the following method signature:
public void makeSandwich(Bread slice1, Bread slice2, List<Filling> fillings, boolean mustard)
I would like to know, at runtime, the value that was passed on to slice2 or any other parameter, the important bit here is that I want to get the value by parameter name.
I know how to get the list of parameter types with getParameterTypes or getGenericParameterTypes.
Ideally I would like to get a list of parameter names instead of types. Is there a way to do so?

Parameter names are available if you have told the compiler to include them (compile with debug information). Spring has ParameterNameDiscoverer which can help you obtain the names. The default implementation uses asm ClassReader to do so.
With javac you should include the -g argument to include debug information. With Eclipse I think it is there by default; it can be configured using the preferences: Java -> Compiler and then enable "Store information about method parameters (usable via reflection)" (see also this answer).
Some frameworks use this. For example spring-mvc has #RequestParam which defaults to the param name, if resolvable. It also supports explicit naming - #RequestParam("foo") in case no debug information is provided.

I have found another solution after marking this question as answered. The solution is Paranamer.
Example:
Method method = Foo.class.getMethod(...);
Paranamer paranamer = new CachingParanamer();
String[] parameterNames = paranamer.lookupParameterNames(method) // throws ParameterNamesNotFoundException if not found
// or ...
parameterNames = paranamer.lookupParameterNames(method, false) // will return null if not found

Since Java 1.8, this can be done as long as the parameter names are in the class files. Using javac this is done passing the -parameters flag. From the javac help
-parameters Generate metadata for reflection on method parameters
From IDEs you will need to look at the compiler settings.
If the parameter names are in the class files then here is an example of doing this
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class ParameterNamesExamples {
public static void main(String[] args) throws Exception {
Method theDoSomethingMethod = ExampleClass.class.getMethods()[0];
// Now loop through the parameters printing the names
for(Parameter parameter : theDoSomethingMethod.getParameters()) {
System.out.println(parameter.getName());
}
}
private class ExampleClass {
public void doSomething(String myFirstParameter, String mySecondParameter) {
// No-op
}
}
}
The output will depend on if the parameter names are in the class files. If they are the output is:
myFirstParameter
mySecondParameter
If not the output is:
arg0
arg1
More information on this from Oracle can be found at Obtaining Names of Method Parameters

In addition to this answer:
"Parameter names are available if you have told the compiler to include them"
If you're using Eclipse go to project -> properties -> Java Compiler -> check "Store information about method parameters (usable via reflection)

In Java parameter names are not available via reflection.

This is not possible. Class files do not contains the argument names, as you can see with your IDE's autocompletion when the source is not available.
Therefore, the reflection API is not able to give out parameter names.

You can simply assign the value of the parameter to another value
Bread slice2;
public void makeSandwich(Bread slice1, Bread slice2, List<Filling> fillings, boolean mustard) {
this.slice2 = slice2;
System.out.println(this.slice2.getSomething());
}

Do you own the code of the method? You could annotate the parameters and pass names as arguments #Param("slice1"). Later you will be able to get the annotation and extract parameter name from it.

Related

Cucumber-java won't use custom #ParameterType

I have a very old cucumber-java project (v. 1.2.2) that I'm trying to update to use the current version (6.10.4). For the most part it's going well, but I'm having trouble converting an old step definition which used the #Delimiter annotation to use a custom parameter type.
This was the old step definition using the delimiter:
#Then("^there are error messages: \"(.*)\"$")
public void assertAllMessages(#Delimiter("; ") List<String> messages) throws Exception {
// ...
}
Since #Delimiter was removed when XStream support was removed from cucumber-java, the documentation says to replace it with a custom #ParameterType. Fair enough. I wrote this one, which should be sufficient to identify a string representation of a semi-colon-separated list and convert it to a List<String>:
#ParameterType("(?:.+;)+.+")
public List<String> stringList(String raw) {
String[] values = raw.split(";");
return Arrays.asList(values);
}
And then I removed the #Delimiter from the step definition, but left it otherwise as-is.
However when I run my test, it fails and indicates that I should register a parameter type for List:
Caused by: java.lang.IllegalArgumentException: Can't transform 'foo; bar; baz' to java.util.List<java.lang.String>
BuiltInParameterTransformer only supports a limited number of class types
Consider using a different object mapper or register a parameter type for java.util.List<java.lang.String>
at io.cucumber.cucumberexpressions.BuiltInParameterTransformer.createIllegalArgumentException(BuiltInParameterTransformer.java:114)
at io.cucumber.cucumberexpressions.BuiltInParameterTransformer.doTransform(BuiltInParameterTransformer.java:33)
at io.cucumber.cucumberexpressions.BuiltInParameterTransformer.transform(BuiltInParameterTransformer.java:22)
at io.cucumber.cucumberexpressions.RegularExpression.lambda$match$0(RegularExpression.java:66)
at io.cucumber.cucumberexpressions.ParameterType$TransformerAdaptor.transform(ParameterType.java:268)
at io.cucumber.cucumberexpressions.ParameterType.transform(ParameterType.java:233)
... 47 more
I know that it has successfully picked up my custom parameter type, because it's in the same file as the step definition. (Further if I create another copy elsewhere in the glue directory, it complains there's already a parameter type named "stringList" registered.)
I also tried changing the regex to just #ParameterType(".*"), but it made no difference.
How do I get Cucumber to use my custom parameter type for converting a string into a List<String>?
Junit 5.8.1 (with vintage engine for 4 support)
Cucumber-java and cucumber-junit 6.10.4
OpenJDK 17
Assume you have this feature file:
Feature: test
Scenario: test scenario
Given: This string: foo; bar; baz
So, to have a proper use of custom parameter types you have to assure two things:
Poprer step definition. Your step definition has to be defined as a Cucumber Expression (not regular expression).
#Given("This string: {list}")
public void givenThisString(List<String> myList){
System.out.println(myList);
}
Proper parameter type. A parameter type defined within some glued class:
#ParameterType("([^;]+);?")
public List<String> list(String[] vals){
return Arrays.asList(vals);
}
Put your attention at that in your step definition you refer to parameter type as {list} so that Cucumber knows where to take a parser. If you do not specify type name explicitly through annotation value, Cucumber takes the type name as the name of a method you have annotated with #ParameterType
Parameter types are part of cucumber-expressions. So instead of writing a regex you should either use a cucumber-expression or use the exact regex from the parameter type in your regular expression.
#ParameterType("(?:.+;)+.+")
public List<String> stringList(String raw) {
String[] values = raw.split(";");
return Arrays.asList(values);
}
#Then("there are error messages: \"{stringList}\"")
public void assertAllMessages(#Delimiter("; ") List<String> messages) throws Exception {
// ...
}

Getting full field type of ICompilationUnit

I need to get type of fields declared in java file (ICompilationUnit).
for (IType type : compilationUnit.getTypes()) {
for (IField iField : type.getFields()) {
typesig = iField.getTypeSignature()
}
}
getTypeSignature() - returns type name, but without the package name.
It's possible to obtain package name using:
IType.resolveType(String typeName)
But this method is heavy and takes some time to complete.
Is there another way to get the full type name (with package)?
When using JDT's Java model, the approach in the question (using IType.resolveType()) is the correct one.
If you need resolved types of many elements consider using an AST with resolved bindings. That way you pay the price for resolving only once.
To use type signature, use this code:
Signature.getSignatureSimpleName(iField.getTypeSignature())

how can spring get args name from the method

In spring, it express the arg-names like this :
#Before(
value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && #annotation(auditable)",
argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code, bean, and jp
}
In the doc , it says you may leave out the name of the parameter from the value of the "argNames" attribute. How can spring get argNames("bean,auditable") from anyPublicMethod?
Did java provide some api to get the parameter names?
I think it use the aspectJ to parse the expression, did aspectJ provide the feature?
In Java 8 we can use reflection to get parameter name, see http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html. Though argument names will be present if the classes have been compiled with -g:vars.
In earlier versions we need to use some tool, like Javassist:
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass c = pool.get("test.Test");
CtMethod m = c.getDeclaredMethod("main");
MethodInfo methodInfo = m.getMethodInfo();
LocalVariableAttribute t = (LocalVariableAttribute) methodInfo.getCodeAttribute().getAttribute(javassist.bytecode.LocalVariableAttribute.tag);
int i = t.nameIndex(0);
String v = methodInfo.getConstPool().getUtf8Info(i);
System.out.println(v);
}
prints
args
Your annotation
#Before(
value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && #annotation(auditable)",
argNames="bean,auditable")
, assuming anyPublicMethod() matches any public method, will match any public method annotated with whatever type a parameter named auditable is and invoked on an object of bound to the parameter named bean.
In your method, a parameter named auditable is of type Auditable, so this #Before advice would match something like
public class SomeComponent {
#Auditable
public void someMethod() {}
}
I will bind the SomeComponent type bean to the bean parameter and the instance for the #Auditable annotation to the auditable parameter when invoking the advice.
Java does not have an API for getting parameter names. Parameter names are also not always present in the byte code. When they are, Spring uses the ASM library to parse the bytecode and retrieve them.
Just a complement to the other answers. Spring documentation is explicit : Spring does its best to retrieve correct parameters :
if using Java 8 it uses Java 8 reflection
if classes are compiled in debug mode, parameter name is present and spring uses it, same if ajc (aspectJ compiler) has been used)
it can eventually uses #Param annotations
it uses the type if not ambiguous

Understanding [[I.class referenced in some Java source

I'm looking at some Java reflection sourcecode that goes like this:
Method fixTransparentPixels = TextureAtlasSprite.class.getDeclaredMethod("fixTransparentPixels", new Class[] { [[I.class });
The method being referenced is declared like so:
private void fixTransparentPixels(int[][] p_147961_1_) {...}
What I do not understand is the [[I.class part. Now, I get that the actual Class[] array is to determine which form of the declared method you want (what parameter types etc.), but what does [[I.class actually mean?
Furthermore, when I try to write this reflection code myself, my IDE gives me syntax errors on the [[I.class bit. Can anyone give me any info on this?
Cheers.
When using getDeclaredMethod(String name, Class<?>... parameterTypes) the parameterTypes must be the class of the parameter (obviously). So in this case fixTransparentPixels require a int[][], so the parameterTypes will be int[][].class.
This will works :
TextureAtlasSprite.class.getDeclaredMethod("fixTransparentPixels", int[][].class);
[[I is the internal name of the class for int[][]:
System.out.println(int[][].class.getName()); outputs [[I
or Class.forName("[[I") == int[][].class.
However, it's illegal to write [[I.class in source code. You should write int[][].class instead.

Java : method that takes in argument any attribute of any class

I need to create a method that takes in argument any attribute of any class. But i dont want it to be of type String, to avoid refactoring problems while renaming an attribute and to get the errors in Markers Tab of eclipse, and not while running my application.
Having a class Person :
public class Person {
private String name;
// other attributes...
// getters and setters...
}
Now the needed method :
void getAnAttributeOfAClass( <which_type_or_class_here?> attr_as_arg){
// Now I need to get the name of attribute that would be of class Strin...
}
Is there a function or a method, by which we can specify an attribute?
For example :
Person.class.name
Would it be of class Property ?
EDIT
More exactly (#Smallhacker answer helped me), I need to verify at compile time if the argument is really an attribute of the specified class.
Person.class.name // no compile time error
Person.class.nameXXX // compile time error
The closest to what you want is Reflection API's Field or JavaBeans Introspector API's PropertyDescriptor.
But usually things like that are not needed in Java projects because there are libraries which handle these concerns.
You could pass a Class object along with a String name, then let your method use Introspector internally to read that property.
Not sure I understand you well, but there is a class java.lang.reflect.Field, that has a method getName() that would give your the name of the field.
In your example, to get field name, you would do: Person.class.getDeclaredField("name").
EDIT: to get the value of a field in an object, you would do: field.get(obj);
OK, let's say You have the following variables:
Person person = ...; // initialized with some Person
Field nameField = Person.class.getDeclaredField("name");
Now to get the name of person, you would do:
String personName = (String)nameField.get(person);
Actually, this would throw an exception because name is a private field. You can however bypass the protection by doing:
nameField.setAccessible(true);
Unfortunately, Java lacks an ability to reference member variables in a way that can be analyzed at compile time.
There may be some kind of library to simplify this somewhat, but it wouldn't provide a full solution due to limitations in the language itself.
Maybe java generics can help you with this.
You can do something like:
class YourClass<E> {
void getAnAttributeOfAClass(E attr_as_arg){
// some code
}
}
someVariable = new YourClass<Person>();
someVariable.getAnAtributeOfAClass(someObject); //this will not compile if someObject is not an instance of Person
But I still don't know what you want to do exactly inside the method.

Categories