6c6990feb7a7792e7097dbdd08060d2aa32d5cec
[idea/community.git] / plugins / groovy / groovy-psi / src / org / jetbrains / plugins / groovy / transformations / impl / autoClone / AutoCloneTransformationSupport.kt
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.plugins.groovy.transformations.impl.autoClone
17
18 import com.intellij.codeInsight.AnnotationUtil
19 import com.intellij.psi.*
20 import com.intellij.psi.impl.light.LightMethodBuilder
21 import org.jetbrains.plugins.groovy.GroovyLanguage
22 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
23 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil
24 import org.jetbrains.plugins.groovy.transformations.AstTransformationSupport
25 import org.jetbrains.plugins.groovy.transformations.TransformationContext
26 import org.jetbrains.plugins.groovy.transformations.plusAssign
27
28 class AutoCloneTransformationSupport : AstTransformationSupport {
29
30   private companion object {
31     val AUTO_CLONE_FQN = "groovy.transform.AutoClone"
32     val ORIGIN_INFO = "created by @AutoClone"
33     val CNSE_FQN = "java.lang.CloneNotSupportedException"
34   }
35
36   override fun applyTransformation(context: TransformationContext) {
37     val annotation = context.getAnnotation(AUTO_CLONE_FQN) ?: return
38
39     context.addInterface(CommonClassNames.JAVA_LANG_CLONEABLE)
40
41     // public T clone() throws CloneNotSupportedException
42     context += LightMethodBuilder(context.manager, "clone").apply {
43       addModifier(PsiModifier.PUBLIC)
44       setMethodReturnType(TypesUtil.createType(context.codeClass))
45       addException(CNSE_FQN)
46       navigationElement = annotation
47       originInfo = ORIGIN_INFO
48     }
49
50     val value = annotation.findDeclaredDetachedValue("style") as? GrReferenceExpression ?: return
51     val constant = value.resolve() as? PsiEnumConstant ?: return
52     if (constant.containingClass?.qualifiedName != "groovy.transform.AutoCloneStyle") return
53     when (constant.name) {
54       "COPY_CONSTRUCTOR" -> {
55         if (context.codeClass.codeConstructors.isEmpty()) {
56           context += LightMethodBuilder(context.codeClass, GroovyLanguage).apply {
57             isConstructor = true
58             addModifier(PsiModifier.PUBLIC)
59             navigationElement = context.codeClass
60           }
61         }
62
63         // protected T(T other)
64         context += LightMethodBuilder(context.codeClass, GroovyLanguage).apply {
65           isConstructor = true
66           addModifier(PsiModifier.PROTECTED)
67           addParameter("other", TypesUtil.createType(context.codeClass))
68           navigationElement = context.codeClass
69           originInfo = ORIGIN_INFO
70         }
71       }
72       "SIMPLE" -> {
73         // protected void cloneOrCopyMembers(T other) throws CloneNotSupportedException
74         context += LightMethodBuilder(context.manager, "cloneOrCopyMembers").apply {
75           addModifier(PsiModifier.PROTECTED)
76           setMethodReturnType(PsiType.VOID)
77           addParameter("other", TypesUtil.createType(context.codeClass))
78           addException(CNSE_FQN)
79           navigationElement = annotation
80           originInfo = ORIGIN_INFO
81         }
82       }
83     }
84   }
85
86   private fun PsiAnnotation.findDeclaredDetachedValue(attributeName: String?): PsiAnnotationMemberValue? {
87     val styleAttribute = AnnotationUtil.findDeclaredAttribute(this, attributeName)
88     return when (styleAttribute) {
89       null -> null
90       is PsiNameValuePair.Detachable -> styleAttribute.detachedValue
91       else -> styleAttribute.value
92     }
93   }
94 }