update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / generation / GenerateMembersUtil.java
blob5e80c2f3ef0aa0bb683b421d11ab23798b8ecaf2
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.codeInsight.generation;
18 import com.intellij.lang.ASTNode;
19 import com.intellij.lang.StdLanguages;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.editor.Editor;
22 import com.intellij.openapi.editor.ScrollType;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.Pair;
25 import com.intellij.psi.*;
26 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
27 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
28 import com.intellij.psi.codeStyle.VariableKind;
29 import com.intellij.psi.javadoc.PsiDocComment;
30 import com.intellij.psi.util.PsiUtil;
31 import com.intellij.psi.util.TypeConversionUtil;
32 import com.intellij.refactoring.util.RefactoringConflictsUtil;
33 import com.intellij.util.VisibilityUtil;
34 import com.intellij.util.IncorrectOperationException;
35 import com.intellij.util.containers.HashMap;
36 import org.jetbrains.annotations.NonNls;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
40 import java.util.Collections;
41 import java.util.List;
42 import java.util.Map;
44 public class GenerateMembersUtil {
45 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateMembersUtil");
47 private GenerateMembersUtil() {
50 @NotNull
51 public static <T extends GenerationInfo> List<T> insertMembersAtOffset(PsiFile file, int offset, @NotNull List<T> memberPrototypes) throws IncorrectOperationException {
52 if (memberPrototypes.isEmpty()) return memberPrototypes;
53 final PsiElement leaf = file.findElementAt(offset);
54 if (leaf == null) return Collections.emptyList();
56 PsiClass aClass = findClassAtOffset(file, leaf);
57 if (aClass == null) return Collections.emptyList();
58 PsiElement anchor = memberPrototypes.get(0).findInsertionAnchor(aClass, leaf);
60 if (anchor instanceof PsiWhiteSpace) {
61 final ASTNode spaceNode = anchor.getNode();
62 anchor = anchor.getNextSibling();
64 assert spaceNode != null;
65 if (spaceNode.getStartOffset() <= offset && spaceNode.getStartOffset() + spaceNode.getTextLength() >= offset) {
66 final ASTNode singleNewLineWhitespace = JavaPsiFacade.getInstance(file.getProject()).getElementFactory().createWhiteSpaceFromText(spaceNode.getText().substring(0, offset - spaceNode.getStartOffset())).getNode();
67 spaceNode.getTreeParent().replaceChild(spaceNode, singleNewLineWhitespace); // See http://jetbrains.net/jira/browse/IDEADEV-12837
71 // Q: shouldn't it be somewhere in PSI?
72 PsiElement element = anchor;
73 while (true) {
74 if (element == null) break;
75 if (element instanceof PsiField || element instanceof PsiMethod || element instanceof PsiClassInitializer) break;
76 element = element.getNextSibling();
78 if (element instanceof PsiField) {
79 PsiField field = (PsiField) element;
80 PsiTypeElement typeElement = field.getTypeElement();
81 if (typeElement != null && !field.equals(typeElement.getParent())) {
82 field.normalizeDeclaration();
83 anchor = field;
87 return insertMembersBeforeAnchor(aClass, anchor, memberPrototypes);
90 @NotNull
91 public static <T extends GenerationInfo> List<T> insertMembersBeforeAnchor(PsiClass aClass, PsiElement anchor, @NotNull List<T> memberPrototypes) throws IncorrectOperationException {
92 boolean before = true;
93 for (T memberPrototype : memberPrototypes) {
94 memberPrototype.insert(aClass, anchor, before);
95 before = false;
96 anchor = memberPrototype.getPsiMember();
98 return memberPrototypes;
101 public static void positionCaret(@NotNull Editor editor, @NotNull PsiElement firstMember, boolean toEditMethodBody) {
102 LOG.assertTrue(firstMember.isValid());
104 if (toEditMethodBody) {
105 PsiMethod method = (PsiMethod) firstMember;
106 PsiCodeBlock body = method.getBody();
107 if (body != null) {
108 PsiElement l = body.getFirstBodyElement();
109 while (l instanceof PsiWhiteSpace) l = l.getNextSibling();
110 if (l == null) l = body;
111 PsiElement r = body.getLastBodyElement();
112 while (r instanceof PsiWhiteSpace) r = r.getPrevSibling();
113 if (r == null) r = body;
115 int start = l.getTextRange().getStartOffset();
116 int end = r.getTextRange().getEndOffset();
118 editor.getCaretModel().moveToOffset(Math.min(start, end));
119 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
120 if (start < end) {
121 //Not an empty body
122 editor.getSelectionModel().setSelection(start, end);
124 return;
128 int offset;
129 if (firstMember instanceof PsiMethod) {
130 PsiMethod method = (PsiMethod) firstMember;
131 PsiCodeBlock body = method.getBody();
132 if (body == null) {
133 offset = method.getTextRange().getStartOffset();
135 else {
136 offset = body.getLBrace().getTextRange().getEndOffset();
139 else {
140 offset = firstMember.getTextRange().getStartOffset();
143 editor.getCaretModel().moveToOffset(offset);
144 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
145 editor.getSelectionModel().removeSelection();
148 public static PsiElement insert(PsiClass aClass, PsiMember member, PsiElement anchor, boolean before) throws IncorrectOperationException {
149 if (member instanceof PsiMethod) {
150 if (!aClass.isInterface()) {
151 final PsiParameter[] parameters = ((PsiMethod)member).getParameterList().getParameters();
152 final boolean generateFinals = CodeStyleSettingsManager.getSettings(aClass.getProject()).GENERATE_FINAL_PARAMETERS;
153 for (final PsiParameter parameter : parameters) {
154 final PsiModifierList modifierList = parameter.getModifierList();
155 assert modifierList != null;
156 modifierList.setModifierProperty(PsiModifier.FINAL, generateFinals);
161 if (anchor != null) {
162 return before ? aClass.addBefore(member, anchor) : aClass.addAfter(member, anchor);
164 else {
165 return aClass.add(member);
169 @Nullable
170 private static PsiClass findClassAtOffset(PsiFile file, PsiElement leaf) {
171 PsiElement element = leaf;
172 while (element != null && !(element instanceof PsiFile)) {
173 if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) {
174 final PsiClass psiClass = (PsiClass)element;
175 if (psiClass.isEnum()) {
176 PsiElement lastChild = null;
177 for (PsiElement child : psiClass.getChildren()) {
178 if (child instanceof PsiJavaToken && ";".equals(child.getText())) {
179 lastChild = child;
180 break;
182 else if (child instanceof PsiJavaToken && ",".equals(child.getText()) || child instanceof PsiEnumConstant) {
183 lastChild = child;
186 if (lastChild != null) {
187 int adjustedOffset = lastChild.getTextRange().getEndOffset();
188 if (leaf.getTextRange().getEndOffset() <= adjustedOffset) return findClassAtOffset(file, file.findElementAt(adjustedOffset));
191 return psiClass;
193 element = element.getParent();
195 return null;
198 public static PsiMethod substituteGenericMethod(PsiMethod method, PsiSubstitutor substitutor) {
199 Project project = method.getProject();
200 PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory();
202 PsiTypeParameter[] typeParams = method.getTypeParameters();
203 try {
204 PsiType returnType = method.getReturnType();
206 PsiMethod newMethod;
207 if (method.isConstructor()) {
208 newMethod = factory.createConstructor();
209 newMethod.getNameIdentifier().replace(factory.createIdentifier(method.getName()));
211 else {
212 newMethod = factory.createMethod(method.getName(), substituteType(substitutor, returnType));
215 RefactoringConflictsUtil.setVisibility(newMethod.getModifierList(), VisibilityUtil.getVisibilityModifier(method.getModifierList()));
217 PsiElement navigationElement = method.getNavigationElement();
218 PsiDocComment docComment = ((PsiDocCommentOwner)navigationElement).getDocComment();
219 if (docComment != null) {
220 newMethod.addAfter(docComment, null);
223 PsiParameter[] parameters = method.getParameterList().getParameters();
224 JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
225 Map<PsiType,Pair<String,Integer>> m = new HashMap<PsiType, Pair<String,Integer>>();
226 for (int i = 0; i < parameters.length; i++) {
227 PsiParameter parameter = parameters[i];
228 final PsiType parameterType = parameter.getType();
229 PsiType substituted = substituteType(substitutor, parameterType);
230 @NonNls String paramName = parameter.getName();
231 final String[] baseSuggestions = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, parameterType).names;
232 boolean isBaseNameGenerated = false;
233 for (String s : baseSuggestions) {
234 if (s.equals(paramName)) {
235 isBaseNameGenerated = true;
236 break;
240 if (paramName == null || isBaseNameGenerated && !substituted.equals(parameterType)) {
241 Pair<String, Integer> pair = m.get(substituted);
242 if (pair != null) {
243 paramName = pair.first + pair.second;
244 m.put(substituted, Pair.create(pair.first, pair.second.intValue() + 1));
246 else {
247 String[] names = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, substituted).names;
248 if (names.length > 0) {
249 paramName = names[0];
250 } else paramName = "p" + i;
252 m.put(substituted, new Pair<String, Integer>(paramName, 1));
256 if (paramName == null) paramName = "p" + i;
258 PsiParameter newParameter = factory.createParameter(paramName, substituted);
259 if (parameter.getLanguage() == StdLanguages.JAVA) {
260 newParameter.getModifierList().replace(parameter.getModifierList());
262 newMethod.getParameterList().add(newParameter);
265 for (PsiTypeParameter typeParam : typeParams) {
266 if (substitutor.substitute(typeParam) != null) newMethod.getTypeParameterList().add(typeParam);
269 PsiClassType[] thrownTypes = method.getThrowsList().getReferencedTypes();
270 for (PsiClassType thrownType : thrownTypes) {
271 newMethod.getThrowsList().add(factory.createReferenceElementByType((PsiClassType)substituteType(substitutor, thrownType)));
273 return newMethod;
275 catch (IncorrectOperationException e) {
276 LOG.error(e);
277 return method;
281 private static PsiType substituteType(final PsiSubstitutor substitutor, final PsiType type) {
282 final PsiType psiType = substitutor.substitute(type);
283 if (psiType != null) return psiType;
284 return TypeConversionUtil.erasure(type);
287 public static PsiSubstitutor correctSubstitutor(PsiMethod method, PsiSubstitutor substitutor) {
288 PsiClass hisClass = method.getContainingClass();
289 PsiTypeParameter[] typeParameters = method.getTypeParameters();
290 if (typeParameters.length > 0) {
291 if (PsiUtil.isRawSubstitutor(hisClass, substitutor)) {
292 substitutor = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createRawSubstitutor(substitutor, typeParameters);
295 return substitutor;
298 public static boolean isChildInRange(PsiElement child, PsiElement first, PsiElement last) {
299 if (child.equals(first)) return true;
300 while (true) {
301 if (child.equals(first)) return false; // before first
302 if (child.equals(last)) return true;
303 child = child.getNextSibling();
304 if (child == null) return false;