Find Class name of Super Keyword in JavaParser - java

I am developing a Java application based on JavaParser. I do not know how to get class name of Super keyword used in a method body. As an example, I need to know Super keyword in the below code is referred to class A.
class A {
public void bar(){...}
}
class B extends A {}
class C extends B {
void foo(){
super.bar() // I want to find that the super keyword is referred to Class A.
}
}
I checked these functions (1, 2, and 3) provided by JavaParser, but none of them worked, all return null.
MethodCallExpr methodCallExpr = ...
Optional<Expression> scope = methodCallExpr.getScope();
SuperExpr superExp = scope.get().asSuperExpr();
1. superExp.findAll(ClassOrInterfaceDeclaration.class); and
2. superExp.getTypeName();
3. superExp.getClassExpr(); //I do not know why this method also returns null

I find the right method.
ResolvedType resolvedType = superExp.calculateResolvedType();
This method works correctly if you also add the JavaSymbolSolver to the parser configuration. JavaSymbolSolver is necessary to resolve references and find relations between nodes.
TypeSolver reflectionTypeSolver = new ReflectionTypeSolver();
TypeSolver javaParserTypeSolver = new JavaParserTypeSolver(projectSourceDir);
CombinedTypeSolver combinedSolver = new CombinedTypeSolver();
combinedSolver.add(reflectionTypeSolver);
combinedSolver.add(javaParserTypeSolver);
ParserConfiguration parserConfiguration = new ParserConfiguration()
.setSymbolResolver(new JavaSymbolSolver(combinedSolver));
SourceRoot sourceRoot = new SourceRoot(projectSourceDir.toPath());
sourceRoot.setParserConfiguration(parserConfiguration);

Related

How can I call a Supplier-based constructor for a recursive set of classes based on generic container?

I have seen advice that a Supplier can be used to create an instance of a parametrized class.
E.g. Here: "Instantiating object of type parameter" or here: "Create instance of generic type in Java?"
How to create the Supplier seems clear from the linked answers:
// Example of creating objects in linked answers
MyClass<StringBuilder> it = new MyClass<>(StringBuilder::new);
SomeContainer<String> stringContainer = new SomeContainer<>(String::new);
but how to use it for a recursive set of classes based on generic container doesn't seem to be clear.
My problem is that this Supplier methods itself has to be called as a parameter of the constructor for each recursive class. Example:
class SetOf<E> {
// Supplier-based code from SO Q #75175
private Supplier<E> supplier;
SetOf(Supplier<E> supplier) { this.supplier = supplier; }
createE() { return supplier.get(); }
addToContainer(key) {
E value = createE(); // I assume that's how it's used?
this.add(key, value);
}
}
The problem here is that if we are building container of containers thusly:
class SetOfThings extends SetOf<Thing> {}
class SetOfSetsOfThings extends SetOf<SetOfThings> {}
class SetOfSetOfSetsOfThings extends SetOf<SetOfSetOfThings> {}
SetOf<SetOfSetOfSetOfThings> root = new SetOf<>(SetOfSetOfThings::new);
So, what we are passing to the Supplier of outer class (SetOfSetOfSetOfThings) is the reference to constructor of inner class (SetOfSetOfThings) - BUT, when that SetOfSetOfThings() constructor will be called, it will be called with no parameters, meaning that there will be NO supplier for SetOfThings set in the SetOfSetOfThings object! (which means we can never create SetOfThings objects).
I suppose we can fix this 3-level chain somehow, by using Lambda syntax and passing a parameter (with value of SetOfThings::new) to SetOfSetOfThings::new in turn. But this only works because our chain is 3 levels deep - what if we have to instantiate 4-level deep chain with root class being SetOfSetOfSetsOfSetsOfThings?
This question is a very narrow Function/Supplier specific version of my more generic earlier question here (where comments pointed to both Supplier as an idea, and the linked answer showing how to use a supplier in general - neither of which work easily in case of recursive class hierarchy).
You're going to need some kind of supplier passed to the constructor at any level, since the Thing at the base level ultimately has to come from somewhere. However, you can use Supplier<Thing> at every level like so:
class SetOfThings extends SetOf<Thing> {
SetOfThings(Supplier<Thing> supplier) {
super(supplier);
}
}
class SetOfSetOfThings extends SetOf<SetOfThings> {
SetOfSetOfThings(Supplier<Thing> supplier) {
super(() -> new SetOfThings(supplier));
}
}
class SetOfSetOfSetOfThings extends SetOf<SetOfSetOfThings> {
SetOfSetOfSetOfThings(Supplier<Thing> supplier) {
super(() -> new SetOfSetOfThings(supplier));
}
}
Then you can do:
SetOf<SetOfSetOfSetOfThings> root = new SetOf<>(() -> new SetOfSetOfSetOfThings(Thing::new));
SetOfSetOfSetOfThings ssst = root.createE();
SetOfSetOfThings sst = ssst.createE();
SetOfThings st = sst.createE();
Thing t = st.createE();
EDIT: If you're just trying to create an instance of recursive SetOf type, you don't need subclasses:
SetOf<SetOf<SetOf<SetOf<Thing>>>> root = new SetOf<>(
() -> new SetOf<>(
() -> new SetOf<>(
() -> new SetOf<>(
Thing::new))));
And with a helper method to make it look nicer:
public static <E> Supplier<SetOf<E>> setsOf(Supplier<E> supplier) {
return () -> new SetOf<>(supplier);
}
SetOf<SetOf<SetOf<SetOf<Thing>>>> root = new SetOf<>(setsOf(setsOf(setsOf(Thing::new))));

Getting behavior of Java's Class<? extends Map> in .NET

I have a generic class in java defined as:
public static class KeyCountMap<T>
{
private Map<T, MutableInt> map = new LinkedHashMap<T, MutableInt>();
// ... rest of the properties...
public KeyCountMap()
{ }
#SuppressWarnings({ "unchecked", "rawtypes" })
public KeyCountMap(Class<? extends Map> mapType) throws InstantiationException, IllegalAccessException
{
map = mapType.newInstance();
}
//... rest of the methods...
}
I have defined same class in .NET as:
public static class KeyCountMap<T>
{
private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
// ... rest of properties...
public KeyCountMap()
{ }
public void KeyCountMap<T>(T obj) where T : Dictionary<T, MutableInt>
{
obj = new T(); // Unable to define new instance of T
map = obj; // Unable to convert T to base class
}
}
And then a method is defined to sort map of type KeyCountMap<T> by value in the descending order . The method is defined as:
public static KeyCountMap<T> SortMapByDescendValue<T>(KeyCountMap<T> _map)
{
List<KeyValuePair<T, MutableInt>> _list = new List<KeyValuePair<T, MutableInt>>(_map.EntrySet());
// whereas _map.EntrySet() return of type HashSet<KeyValuePair<T, MutableInt>>
_list = _list.OrderByDescending(_x => _x.Value).ToList();
KeyCountMap<T> _result = new KeyCountMap<T>();
foreach (KeyValuePair<T, MutableInt> _entry in _list)
{
_result.Put(_entry.Key, _entry.Value);
}
return _result;
}
How can I get corrected the class defined in .NET ?
I assume you know Java erases any generic type information after compiling (there's metadata for variables, but actual objects are void of generic type information). Moreover, your code is not type safe:
#SuppressWarnings({ "unchecked", "rawtypes" })
You're using this because you're creating a non-parameterized instance of Map.
In .NET, you don't get around the type system like this, because generic type information is kept and used at runtime.
Let's see your C# code:
public static class KeyCountMap<T>
A static class in C# is a class that cannot be instanced, it's used for its static members alone. I think you don't want this. Perhaps KeyCountMap is a static nested class in Java, as opposed to an inner class.
In C#, you don't have inner classes. Nested classes don't share data with an instance of the containing class, it's as if the name of the containing class is part of the namespace for the nested class. So, you don't need, and actually don't want, the static keyword here.
{
private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
In .NET, Dictionary is a class. To keep the intent, you should use IDictionary, the corresponding interface, as the type for the map field.
// ... rest of properties...
public KeyCountMap()
{ }
public void KeyCountMap<T>(T obj) where T : Dictionary<T, MutableInt>
Why the void return type, isn't this a constructor?
In C#, constructors can't be generic. You probably want a Type.
Your C# code just doesn't make sense, so here's what you could do:
public KeyCountMap(Type dictionaryType)
{
if (!typeof(IDictionary<T, MutableInt>).IsAssignableFrom(dictionaryType))
{
throw new ArgumentException("Type must be a IDictionary<T, MutableInt>", nameof(dictionaryType));
}
map = (IDictionary<T, MutableInt>)Activator.CreateInstance(dictionaryType);
}
}
We're checking the type before creating an instance. If we didn't, we would create an instance, the cast would fail and the assignment wouldn't even happen, so the new instance would just be garbage.
It may be that the actual instance will be a proxy; if so, you may not want to check the type before creating an instance.
You can't just copy-paste Java as C# (or vice-versa) and expect to make just a few changes until it works, for some definition of works, e.g. it compiles. The languages are not that similar, and chances are that too many subtle things are wrong.
This approach might be fun at first, but you'll stumble so often it will soon stop being any fun at all. You should learn the basics and understand the way things are done in the target language before you start translating code line-by-line. Many times, you may find that something you had to do in one environment already exists in the other or vice-versa, or that something may take more or less steps to do in the other, etc.
In this particular case, Java made Class be a generic class, while .NET kept Type a non-generic class. In .NET only interfaces and delegates may state generic type covariance or contravariance. This is rather restrictive anyway, if Type was generic, the intended uses could be either covariant or contravariant. But remember that in Java, a generic Class<T> at runtime is as good as Class, it only has any value at compile time and you can tell the compiler you know better anyway, just like you did.
There are two problems. First, you need to tell the compiler that T has a parameterless constructor, so you can call new T(). You can do that by providing the new() argument to the class definition.
You also have to tell the compiler that T is actually the dictionary you are trying to assign, so we have to extend the class a little more:
public class KeyCountMap<K>
{
private Dictionary<K, MutableInt> map = new Dictionary<K, MutableInt>();
// ... rest of properties...
Note that K is the key type of the dictionary, which you didn't specify yet.
Second, the T in your method can be another T than in your class. Omitting that will do the trick:
public void Map()
{
var obj = new Dictionary<K, MutableInt>(); // Unable to define new instance of T
map = obj; // Unable to convert T to base class
}
Maybe this is what you want?
public class KeyCountMap<T>
where T : new()
{
private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
// ... rest of properties...
public KeyCountMap()
{ }
public KeyCountMap(T obj)
{
obj = new T();
map = (Dictionary<T, MutableInt>)(object)obj;
}
}

Why do I get NoSuchMethodException?

I keep getting NoSuchMethodException in this line:
float modif = (float)sc.affectingObject.getClass().getMethod(sc.affectingMethodName, (Class<?>[])null).invoke(sc.affectingObject, (Object[])null);
where sc is an instance of class SubChange:
class SubChange implements Serializable {
String changeType;
Serializable affectingObject;
String affectingFieldName;
String affectingMethodName;
public SubChange(String chanType, Serializable affingObj, String affingFM) {
try{
changeType = chanType;
affectingObject = affingObj;
//deciding whether affingFM provides a field name or a method name
for (Field f : affingObj.getClass().getDeclaredFields()) {
if (f.getName().equals(affingFM) == true) {
affectingFieldName = affingFM;
break;
}
}
if (affectingFieldName == null) {
affectingMethodName = affingFM;
}
} catch(Exception e) {
e.printStackTrace();
}
}
//other class methods
}
whose instance has been initialized like this:
new SubChange("first", physio, "calcTotalFatigue")
where physio is an instance of inner class Physiology belonging to class Hm, while the SubChange constructor is being called from another inner class of the same Hm class.
Needless to say that method calcTotalFatigue() of physio exists.
Can anyone, please, suggest what I am doing wrong?
ANSWER
The error was caused by the wrong access modifier of the method calcTotalFatigue() which I naively omitted in the description of the problem. Making the method public solved the problem.
The exception is being thrown by this part:
sc.affectingObject.getClass().getMethod(
sc.affectingMethodName, (Class<?>[]) null)
and it means that the getMethod call cannot find the method that you requested.
I can't determine why from looking at your code, but I suggest that you print out the classname of the class being returned by sc.affectingObject.getClass(), print out the value of sc.affectingMethodName, and double check that the actual class (or a superclass) defines the method you are looking for.
Needless to say that method calcTotalFatigue() of physio exists.
Needless to say ... you might be wrong about that!
UPDATE
You asked:
Can it be something with access level modifiers?
The javadoc says:
"Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object."
So ... yes, that could be the problem. If you want to retrieve non-public methods, then consider using getDeclaredMethod instead of getMethod.

Java cast base class to an extended class

i have a class that is used widely in my project as some sort of field holder. Something like:
class A
{
private String field = null;
private String field2 = null;
private String field3 = null;
// and its generic Getters and Setters
}
In certain part of code i need add additional fields to this class. So i did this
class B extends A
{
private String fieldInB = null;
// and its Getters and Setters
}
in my function i thought i could easily do
public void foo( A a )
{
B b = (B)a;
}
And i could have all the fields written in aobject and i could easily set only field in b and use it. This seams like a common problem, but i just don't know how to do it except with an very ugly approach:
public B( A a )
{
// copy all the fields one at the time
}
You areconfusing different parts of Java:
B b = (B)a;
this is classical class cast, but to work with B class object you need:
1. be sure that a is of B class (check it with instanceof java keyword:
if (a instanceof B) {
B b = (B) a;
}
2. or wrap a in B class object (create B class object with copying fields from a).
PS in most of Java coding conventions it is recommended to fill fields by concrete values only (and not fill with default JavaVM values - nulls)
Comfortable way to copy A class fields to new instance:
public A (A a) {
this.field = a.field;
this.field2 = a.field2;
this.field3 = a.field3;
}
and for B class:
public B (A a) {
super(a);
}
Another way - some libraries that will work with A class and B class as with beans. Sample of this libraries you can find in Toilal's answer
You could use Dozer. It allows to map bean property values from one bean class to another.
Hai john Actually i didn't get your exact requirement. I recon the way you have written this code is not right.
Private variable cant be inherited.If you need to extend values to your subclass you should have declared those variables as public.
public B(A a)
{
super.field=a.field;
super.field2=a.field2;
super.field3=a.field3;
}

Accessing current object's sibling method

I have two (sibling) classes, both is subclass of the same parent. Parent contains all the methods that is shared by the two sibling, and the sibling classes contain only methods that are not shared and has different implementations.
Example,
class Parent() {
}
class Sibling1 extends Parent() {
byte[] sharedSecret;
int sharedSecretLength;
public generateKey() {
sharedSecret = keyAgree.generateSecret());
sharedSecretLength = sharedSecret.length);
}
}
class Sibling2 extends Parent() {
byte[] sharedSecret2;
int sharedSecretLength2;
public generateKey() {
sharedSecret2 = new byte[sharedSecretLength];
sharedSecretLength2 = keyAgree.generateSecret(sharedSecret2, 0);
}
public int getSharedSecretLength() {
return sharedSecretLength();
}
}
As you can see, both contains same method but implemented differently. My question is, if objects of both class (sibling1 and sibling2) are created AND obj2 generateKey to be generated successfully depends on sharedSecretLength of obj1. Example,
Parent obj1 = new Sibling1();
Parent obj2 = new Sibling2();
obj1 is instantiated in different class (Server class that I created) and obj2 in different class (Client that I created). If obj1 invoke it's own generateKey --> ((Sibling1)obj1).generateKey(), how can I use use getSharedSecretLength on the same object (obj1) to relay the needed information over to obj2's generateKey to generate successfully? I tried something like (in obj2's generateKey() ):
sharedSecret2 = new byte[Sibling1.sharedSecretLength];
...and it didn't work. Creating Sibling1 obj1 inside of Sibling2 class and then call it that way, for example,
Sibling1 xx = null;
.
.
sharedSecret2 = new byte[((Sibling1)xx).sharedSecretLength];
doesn't work because xx is new object. I am trying to use the old obj in which it generated it's key and contains sharedSecretLength that is not 0
If both obj1 and obj2 were created in the same class, it would have been easier.
Please help point me to the right direction
Instead of
((Sibling1)xx).sharedSecretLength
call it like
((Sibling1)xx).getSharedSecretLength()
In java, you have pass the empty parenthesis if it doesn't accept parameter and it doesnt work by field name, you will have to give the same method name as to what you have defined.
It sounds like you should refactor like this so that the generateKey method on Sibling2 has the argument it requires:
Sibling1 obj1 = new Sibling1();
obj1.generateKey();
Siblibg2 obj2 = new Sibling2();
obj2.generateKey(obj1.getSharedSecretLength());

Categories