forked from projectlombok/lombok
-
Notifications
You must be signed in to change notification settings - Fork 0
/
BaseDelombokHandler.java
222 lines (184 loc) · 9.2 KB
/
BaseDelombokHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package de.plushnikov.intellij.plugin.action.delombok;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import de.plushnikov.intellij.plugin.processor.AbstractProcessor;
import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder;
import de.plushnikov.intellij.plugin.settings.ProjectSettings;
import de.plushnikov.intellij.plugin.util.PsiMethodUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class BaseDelombokHandler {
private final boolean processInnerClasses;
private final Collection<AbstractProcessor> lombokProcessors;
protected BaseDelombokHandler(AbstractProcessor... lombokProcessors) {
this(false, lombokProcessors);
}
protected BaseDelombokHandler(boolean processInnerClasses, AbstractProcessor... lombokProcessors) {
this.processInnerClasses = processInnerClasses;
this.lombokProcessors = Arrays.asList(lombokProcessors);
}
public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull PsiClass psiClass) {
if (psiFile.isWritable()) {
invoke(project, psiClass, processInnerClasses);
finish(project, psiFile);
}
}
public void invoke(@NotNull Project project, @NotNull PsiJavaFile psiFile) {
for (PsiClass psiClass : psiFile.getClasses()) {
invoke(project, psiClass, true);
}
finish(project, psiFile);
}
private void invoke(Project project, PsiClass psiClass, boolean processInnerClasses) {
Collection<PsiAnnotation> processedAnnotations = new HashSet<PsiAnnotation>();
// get all inner classes before first lombok processing
final PsiClass[] allInnerClasses = psiClass.getAllInnerClasses();
for (AbstractProcessor lombokProcessor : lombokProcessors) {
processedAnnotations.addAll(processClass(project, psiClass, lombokProcessor));
}
if (processInnerClasses) {
for (PsiClass innerClass : allInnerClasses) {
//skip our self generated classes
if (!(innerClass instanceof LombokLightClassBuilder)) {
invoke(project, innerClass, processInnerClasses);
}
}
}
deleteAnnotations(processedAnnotations);
}
private void finish(Project project, PsiFile psiFile) {
JavaCodeStyleManager.getInstance(project).optimizeImports(psiFile);
UndoUtil.markPsiFileForUndo(psiFile);
}
private Collection<PsiAnnotation> processClass(@NotNull Project project, @NotNull PsiClass psiClass, @NotNull AbstractProcessor lombokProcessor) {
Collection<PsiAnnotation> psiAnnotations = lombokProcessor.collectProcessedAnnotations(psiClass);
final List<? super PsiElement> psiElements = lombokProcessor.process(psiClass);
ProjectSettings.setLombokEnabledInProject(project, false);
try {
for (Object psiElement : psiElements) {
final PsiElement element = rebuildPsiElement(project, (PsiElement) psiElement);
if (null != element) {
psiClass.add(element);
}
}
} finally {
ProjectSettings.setLombokEnabledInProject(project, true);
}
return psiAnnotations;
}
public Collection<PsiAnnotation> collectProcessableAnnotations(@NotNull PsiClass psiClass) {
Collection<PsiAnnotation> result = new ArrayList<PsiAnnotation>();
for (AbstractProcessor lombokProcessor : lombokProcessors) {
result.addAll(lombokProcessor.collectProcessedAnnotations(psiClass));
}
return result;
}
private PsiElement rebuildPsiElement(@NotNull Project project, PsiElement psiElement) {
if (psiElement instanceof PsiMethod) {
return rebuildMethod(project, (PsiMethod) psiElement);
} else if (psiElement instanceof PsiField) {
return rebuildField(project, (PsiField) psiElement);
} else if (psiElement instanceof PsiClass) {
return rebuildClass(project, (PsiClass) psiElement);
}
return null;
}
private PsiClass rebuildClass(@NotNull Project project, @NotNull PsiClass fromClass) {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
PsiClass resultClass = elementFactory.createClass(StringUtil.defaultIfEmpty(fromClass.getName(), "UnknownClassName"));
copyModifiers(fromClass.getModifierList(), resultClass.getModifierList());
rebuildTypeParameter(fromClass, resultClass);
for (PsiField psiField : fromClass.getFields()) {
resultClass.add(rebuildField(project, psiField));
}
for (PsiMethod psiMethod : fromClass.getMethods()) {
resultClass.add(rebuildMethod(project, psiMethod));
}
return (PsiClass) CodeStyleManager.getInstance(project).reformat(resultClass);
}
private PsiMethod rebuildMethod(@NotNull Project project, @NotNull PsiMethod fromMethod) {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
final PsiMethod resultMethod;
final PsiType returnType = fromMethod.getReturnType();
if (null == returnType) {
resultMethod = elementFactory.createConstructor(fromMethod.getName());
} else {
resultMethod = elementFactory.createMethod(fromMethod.getName(), returnType);
}
rebuildTypeParameter(fromMethod, resultMethod);
final PsiClassType[] referencedTypes = fromMethod.getThrowsList().getReferencedTypes();
if (referencedTypes.length > 0) {
PsiJavaCodeReferenceElement[] refs = new PsiJavaCodeReferenceElement[referencedTypes.length];
for (int i = 0; i < refs.length; i++) {
refs[i] = elementFactory.createReferenceElementByType(referencedTypes[i]);
}
resultMethod.getThrowsList().replace(elementFactory.createReferenceList(refs));
}
for (PsiParameter parameter : fromMethod.getParameterList().getParameters()) {
PsiParameter param = elementFactory.createParameter(parameter.getName(), parameter.getType());
final PsiModifierList parameterModifierList = parameter.getModifierList();
if (parameterModifierList != null) {
PsiModifierList modifierList = param.getModifierList();
for (PsiAnnotation originalAnnotation : parameterModifierList.getAnnotations()) {
final PsiAnnotation annotation = modifierList.addAnnotation(originalAnnotation.getQualifiedName());
for (PsiNameValuePair nameValuePair : originalAnnotation.getParameterList().getAttributes()) {
annotation.setDeclaredAttributeValue(nameValuePair.getName(), nameValuePair.getValue());
}
}
modifierList.setModifierProperty(PsiModifier.FINAL, parameterModifierList.hasModifierProperty(PsiModifier.FINAL));
}
resultMethod.getParameterList().add(param);
}
final PsiModifierList fromMethodModifierList = fromMethod.getModifierList();
final PsiModifierList resultMethodModifierList = resultMethod.getModifierList();
copyModifiers(fromMethodModifierList, resultMethodModifierList);
for (PsiAnnotation psiAnnotation : fromMethodModifierList.getAnnotations()) {
final PsiAnnotation annotation = resultMethodModifierList.addAnnotation(psiAnnotation.getQualifiedName());
for (PsiNameValuePair nameValuePair : psiAnnotation.getParameterList().getAttributes()) {
annotation.setDeclaredAttributeValue(nameValuePair.getName(), nameValuePair.getValue());
}
}
PsiCodeBlock body = fromMethod.getBody();
if (null != body) {
resultMethod.getBody().replace(body);
}
return (PsiMethod) CodeStyleManager.getInstance(project).reformat(resultMethod);
}
private void rebuildTypeParameter(@NotNull PsiTypeParameterListOwner listOwner, @NotNull PsiTypeParameterListOwner resultOwner) {
final PsiTypeParameterList fromMethodTypeParameterList = listOwner.getTypeParameterList();
if (listOwner.hasTypeParameters() && null != fromMethodTypeParameterList) {
PsiTypeParameterList typeParameterList = PsiMethodUtil.createTypeParameterList(fromMethodTypeParameterList);
if (null != typeParameterList) {
final PsiTypeParameterList resultOwnerTypeParameterList = resultOwner.getTypeParameterList();
if (null != resultOwnerTypeParameterList) {
resultOwnerTypeParameterList.replace(typeParameterList);
}
}
}
}
private void copyModifiers(PsiModifierList fromModifierList, PsiModifierList resultModifierList) {
for (String modifier : PsiModifier.MODIFIERS) {
resultModifierList.setModifierProperty(modifier, fromModifierList.hasModifierProperty(modifier));
}
}
private PsiField rebuildField(@NotNull Project project, @NotNull PsiField fromField) {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
final PsiField resultField = elementFactory.createField(fromField.getName(), fromField.getType());
copyModifiers(fromField.getModifierList(), resultField.getModifierList());
resultField.setInitializer(fromField.getInitializer());
return (PsiField) CodeStyleManager.getInstance(project).reformat(resultField);
}
private void deleteAnnotations(Collection<PsiAnnotation> psiAnnotations) {
for (PsiAnnotation psiAnnotation : psiAnnotations) {
psiAnnotation.delete();
}
}
}