IntelliJ Plugin Development: how to make class extend another class - java

I'm implementing a plugin in which I need to add extends clause for an existing class.
I have PsiClass instance representing say MyClass.
There is an API that allows to get all the classes that MyClass extends:
PsiReferenceList extendsList = psiClass.getExtendsList()
And theoretically I can add something to it and that will work.
Problem: PsiReferenceList.add() consumes PsiElement and I don't know how to create an object of PsiElement having fully qualified name of the class I want to use.
More specifically, how to transform string com.mycompany.MyAbstractClass to PsiElement representing this class?
Update:
I managed to achieve the result using the following logic:
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiReferenceList extendsList = aClass.getExtendsList();
PsiShortNamesCache instance = PsiShortNamesCache.getInstance(project);
PsiClass[] abstractClasses = instance.getClassesByName(
"MyAbstractClass",
GlobalSearchScope.allScope(project)
);
PsiJavaCodeReferenceElement referenceElement = factory
.createClassReferenceElement(abstractClasses[0]);
extendsList.add(referenceElement);
It works but I guess there should be more optimal way.

You can make a String which is the code you want to generate, like
String code = "class A extends B { }"
Then, use this code to convert text into PsiElement:
PsiElement fromText(String code, Project project) {
return PsiFileFactory
.getInstance(project)
.createFileFromText(JavaLanguage.INSTANCE, code)
.getFirstChild()
}
And you'll get the corresponding PsiElement.
Then, myClass.replace(fromText(code)).
BTW you can also do classNamePsiElement.addAfter(fromText("extends Xxx")) which is considered more efficient.

Related

Insert code from Method into new class

I'm trying to make a library on top of bytebuddy that can be used to perform runtime transformations on a class. The API that I'm making isn't directly operable with ByteBuddy to insert method code into transformed classes, (I want unannotated methods to be added brand new to the transformed class, and methods annotated with #Inject to be injected into the head or tail of an existing method), My solution to the lack of interoperability was to create a temporary class using ByteBuddy, that transforms between my higher level API, and the Advice API from ByteBuddy. But I can't figure out how I could insert all the code from a method in the higher level API into methods defined in the lower level one, since I can't actually use Advice to do it. Is there a way I could do this?
This is the code that I've come up with to this point
fun apply(vararg transformerClasses: Class<*>) {
for(clazz in transformerClasses) {
val builder = ByteBuddy()
.subclass(clazz)
val mixinAnnotation = clazz.getAnnotation(Mixin::class.java) as? Mixin ?: throw RuntimeException("Passed transformer class without #Mixin annotation")
val mixinTarget = mixinAnnotation.target.java
for(method in clazz.declaredMethods) {
if(method.annotations.isEmpty()) {
builder.defineMethod(method.name, method.returnType, Visibility.PUBLIC, Ownership.STATIC)
.intercept(???)
}
}
}
}
There is not really a good way to do so but one solution would be to creste a TypeDescription instance where you translate your custom annotations to those of Byte Buddy.
You could use an implementation of InstrumentedType for creating such an object.
Byte Buddy still reads the byte code from the correct class file if the class name matches. There is no consistency check between the class file and the type description.

Dependency injection into scala objects (not classes)

I have an import "import play.api.libs.ws.WSClient" which i want to use within my object
Object X {
...
}
But this doesn't seem to be available inside my object. I see that dependency injection is only available for classes. How do i get this to work?
Injecting a dependency into an object is impossible.
You have two options:
Ugly and deprecated: Access the injector via the global application:
val wsClient = Play.current.injector.instanceOf[WSClient]
Way to go if your code needs to live in an object: Pass the dependency in as a parameter. However this just defers the problem to the caller.
def myMethod(wsClient: WSClient) = // foo
If youre working with a legacy application where you have objects and need an injected dependency, I think one way to "improve" the situation and make a step into the right direction is to provide access to an injected class like so:
object MyObject {
private def instance = Play.current.injector.instanceOf[MyObject]
def myMethod(param: String): String =
instance.myMethod(param)
}
class MyObject #Inject() (wsClient: WSClient) {
def myMethod(param: String): String =
// foo
}
This allows legacy code to access the methods via object, while new code can inject the dependency. You may also annotate the method on the object as deprecated so that users know.

nameof equivalent in Java

C# 6.0 introduced the nameof() operator, that returns a string representing the name of any class / function / method / local-variable / property identifier put inside it.
If I have a class like this:
class MyClass
{
public SomeOtherClass MyProperty { get; set; }
public void MyMethod()
{
var aLocalVariable = 12;
}
}
I can use the operator like this:
// with class name:
var s = nameof(MyClass); // s == "MyClass"
// with properties:
var s = nameof(MyClass.OneProperty); // s == "OneProperty"
// with methods:
var s = nameof(MyClass.MyMethod); // s == "MyMethod"
// with local variables:
var s = nameof(aLocalVariable); // s == "aLocalVariable".
This is useful since the correct string is checked at compile time. If I misspell the name of some property/method/variable, the compiler returns an error. Also, if I refactor, all the strings are automatically updated. See for example this documentation for real use cases.
Is there any equivalent of that operator in Java? Otherwise, how can I achieve the same result (or similar)?
It can be done using runtime byte code instrumentation, for instance using Byte Buddy library.
See this library: https://github.com/strangeway-org/nameof
The approach is described here: http://in.relation.to/2016/04/14/emulating-property-literals-with-java-8-method-references/
Usage example:
public class NameOfTest {
#Test
public void direct() {
assertEquals("name", $$(Person.class, Person::getName));
}
#Test
public void properties() {
assertEquals("summary", Person.$(Person::getSummary));
}
}
Sadly, there is nothing like this. I had been looking for this functionality a while back and the answer seemed to be that generally speaking, this stuff does not exist.
See Get name of a field
You could, of course, annotate your field with a "Named" annotation to essentially accomplish this goal for your own classes. There's a large variety of frameworks that depend upon similar concepts, actually. Even so, this isn't automatic.
You can't.
You can get a Method or Field using reflection, but you'd have to hardcode the method name as a String, which eliminates the whole purpose.
The concept of properties is not built into java like it is in C#. Getters and setters are just regular methods. You cannot even reference a method as easily as you do in your question. You could try around with reflection to get a handle to a getter method and then cut off the get to get the name of the "property" it resembles, but that's ugly and not the same.
As for local variables, it's not possible at all.
You can't.
If you compile with debug symbols then the .class file will contain a table of variable names (which is how debuggers map variables back to your source code), but there's no guarantee this will be there and it's not exposed in the runtime.
I was also annoyed that there is nothing comparable in Java, so I implemented it myself: https://github.com/mobiuscode-de/nameof
You can simply use it like this:
Name.of(MyClass.class, MyClass::getProperty)
which would just return the String
"property"
It's also on , so you can add it to your project like this:
<dependency>
<groupId>de.mobiuscode.nameof</groupId>
<artifactId>nameof</artifactId>
<version>1.0</version>
</dependency>
or for Gradle:
implementation 'de.mobiuscode.nameof:nameof:1.0'
I realize that it is quite similar to the library from strangeway, but I thought it might be better not to introduce the strange $/$$ notation and enhanced byte code engineering. My library just uses a proxy class on which the getter is called on to determine the name of the passed method. This allows to simply extract the property name.
I also created a blog post about the library with more details.
Lombok has an experimental feature #FieldNameConstants
After adding annotation you get inner type Fields with field names.
#FieldNameConstants
class MyClass {
String myProperty;
}
...
String s = MyClass.Fields.myProperty; // s == "myProperty"

C# extension methods in Java using Scala

I need to create some extension methods in my Java code. I've read some posts here in SO and people suggest XTend or Scala in order to achieve this.
Now, my question would be.. if i write kind of an Adapter layer in Scala (adding there my extension methods) and then using that project as a dependency for my own Java project, are those extended methods available for me to use, or they are defined just for the 'scope of Scala project' and then the JVM output cannot provide those new methods to the other project using it?
EDIT:
What i need to do is to extend a full hierarchy of classes in a given library and give some new functionality. As for Java's first approach I should extend every class in that hierarchy creating my own hierarchy of extended classes adding the new method there. I would like to avoid this and give the final user the sense of native functionality in the original hierarchy.
Regards.
As mentioned above in the comments, it is very close to C# but not exactly there because of the type erasure. For example, this works fine:
object myLibExtensions {
implicit class TypeXExtension( val obj: TypeX ) extends AnyRef {
def myCustomFunction( a: String ): String = {
obj.someMethod(a)
}
}
}
It will act somewhat similar to C# extension methods, i.e. create static method wrappers in reasonable cases (but not always).
The only thing I am missing in Scala is that you can't (or at least I couldn't figure out how to) return the values of the types being extended. For example, assume I want to have something like an extension method "withMeta" that works as follows:
class TypeY extends TypeX { def methodOfY(...) ...}
var y: TypeY = ....
y.withMeta(...).methodOfY(...)
The following didn't work for me:
object myLibExtensions {
private val something = ....
implicit class Extension[T<:TypeX]( val obj: T ) extends AnyRef {
def withMeta( meta: Meta[T] ): T = {
something.associateMeta(obj,meta)
val
}
}
}
... because T is being erased to TypeX. So effectively you will have to write extensions for all specific leaf classes of the hierarchy in this case, which is sad.

Java class implement reflected interface

Is there some way to tell java to think that Object passed to reflected method implements interface of input attribute of method?
public class Debuger /*implements IDebuger*/{
...
}
and this Debuger I need to use in reflected method someDocument.attachDebuger(IDebuger).
I know the structure of IDebuger interface, but I cant just simply write implements IDebuger since it is not in my project.
I want to be able to call something similar
Debuger dbg = new Debuger();
Class theClassINeedToImplement = ...;
Object document = ...;
Class docClass = document.GetClass();
/*
HERE call something like
Object Idbg = theClassINeedToImplement.ForceImplementInterface(dbg);
*/
Method m = docClass.getMethod("attachDebuger", theClassINeedToImplement);
m.invoke(document, Idbg);
I think you need to import IDebuger to your project and then implement it with Debuger. Otherwise the method won't know it is being given an IDebuger, and that will cause a compile error.
Even if its in an external jar, you can import it and use the interface. In Eclipse, right click the project, then select build path, then add external archives. I hope that works.

Categories

Resources