03e6ad8ba57972c0b9dcc4873d7eaeff953715f5
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / PsiElementFactoryImpl.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.psi.impl;
3
4 import com.intellij.lang.*;
5 import com.intellij.lang.java.lexer.JavaLexer;
6 import com.intellij.lang.java.parser.JavaParser;
7 import com.intellij.lang.java.parser.JavaParserUtil;
8 import com.intellij.lexer.Lexer;
9 import com.intellij.openapi.project.Project;
10 import com.intellij.pom.java.LanguageLevel;
11 import com.intellij.psi.*;
12 import com.intellij.psi.codeStyle.CodeStyleManager;
13 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
14 import com.intellij.psi.codeStyle.JavaCodeStyleSettingsFacade;
15 import com.intellij.psi.impl.light.*;
16 import com.intellij.psi.impl.source.*;
17 import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
18 import com.intellij.psi.impl.source.tree.FileElement;
19 import com.intellij.psi.impl.source.tree.TreeElement;
20 import com.intellij.psi.javadoc.PsiDocTag;
21 import com.intellij.psi.search.GlobalSearchScope;
22 import com.intellij.psi.tree.IElementType;
23 import com.intellij.psi.util.PsiUtil;
24 import com.intellij.util.ConcurrencyUtil;
25 import com.intellij.util.IncorrectOperationException;
26 import com.intellij.util.containers.ContainerUtil;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import java.io.IOException;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.concurrent.ConcurrentMap;
34
35 public class PsiElementFactoryImpl extends PsiJavaParserFacadeImpl implements PsiElementFactory {
36   private final ConcurrentMap<LanguageLevel, PsiClass> myArrayClasses = ContainerUtil.newConcurrentMap();
37   private final ConcurrentMap<GlobalSearchScope, PsiClassType> myCachedObjectType = ContainerUtil.createConcurrentSoftMap();
38
39   public PsiElementFactoryImpl(@NotNull PsiManagerEx manager) {
40     super(manager);
41     manager.registerRunnableToRunOnChange(myCachedObjectType::clear);
42   }
43
44   @NotNull
45   @Override
46   public PsiClass getArrayClass(@NotNull LanguageLevel languageLevel) {
47     return myArrayClasses.computeIfAbsent(languageLevel, this::createArrayClass);
48   }
49
50   private PsiClass createArrayClass(LanguageLevel level) {
51     String text = level.isAtLeast(LanguageLevel.JDK_1_5) ?
52                   "public class __Array__<T> {\n public final int length;\n public T[] clone() {}\n}" :
53                   "public class __Array__{\n public final int length;\n public Object clone() {}\n}";
54     PsiClass psiClass = ((PsiExtensibleClass)createClassFromText(text, null)).getOwnInnerClasses().get(0);
55     ensureNonWritable(psiClass);
56     PsiFile file = psiClass.getContainingFile();
57     file.clearCaches();
58     PsiUtil.FILE_LANGUAGE_LEVEL_KEY.set(file, level);
59     return psiClass;
60   }
61
62   private static void ensureNonWritable(PsiClass arrayClass) {
63     try {
64       arrayClass.getContainingFile().getViewProvider().getVirtualFile().setWritable(false);
65     }
66     catch (IOException ignored) {}
67   }
68
69   @NotNull
70   @Override
71   public PsiClassType getArrayClassType(@NotNull PsiType componentType, @NotNull LanguageLevel languageLevel) {
72     PsiClass arrayClass = getArrayClass(languageLevel);
73     PsiTypeParameter[] typeParameters = arrayClass.getTypeParameters();
74
75     PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
76     if (typeParameters.length == 1) {
77       substitutor = substitutor.put(typeParameters[0], componentType);
78     }
79
80     return createType(arrayClass, substitutor);
81   }
82
83   @NotNull
84   @Override
85   public PsiClassType createType(@NotNull PsiClass resolve, @NotNull PsiSubstitutor substitutor) {
86     return new PsiImmediateClassType(resolve, substitutor);
87   }
88
89   @NotNull
90   @Override
91   public PsiClassType createType(@NotNull PsiClass resolve, @NotNull PsiSubstitutor substitutor, @Nullable LanguageLevel languageLevel) {
92     return new PsiImmediateClassType(resolve, substitutor, languageLevel);
93   }
94
95   @NotNull
96   @Override
97   public PsiClassType createType(@NotNull PsiClass resolve,
98                                  @NotNull PsiSubstitutor substitutor,
99                                  @Nullable LanguageLevel languageLevel,
100                                  @NotNull PsiAnnotation[] annotations) {
101     return new PsiImmediateClassType(resolve, substitutor, languageLevel, annotations);
102   }
103
104   @NotNull
105   @Override
106   public PsiClass createClass(@NotNull String name) throws IncorrectOperationException {
107     return createClassInner("class", name);
108   }
109
110   @NotNull
111   @Override
112   public PsiClass createInterface(@NotNull String name) throws IncorrectOperationException {
113     return createClassInner("interface", name);
114   }
115
116   @NotNull
117   @Override
118   public PsiClass createEnum(@NotNull String name) throws IncorrectOperationException {
119     return createClassInner("enum", name);
120   }
121
122   @NotNull
123   @Override
124   public PsiClass createAnnotationType(@NotNull String name) throws IncorrectOperationException {
125     return createClassInner("@interface", name);
126   }
127
128   private PsiClass createClassInner(String type, String name) {
129     PsiUtil.checkIsIdentifier(myManager, name);
130     PsiJavaFile aFile = createDummyJavaFile("public " + type +  " " +  name +  " { }");
131     PsiClass[] classes = aFile.getClasses();
132     if (classes.length != 1) {
133       throw new IncorrectOperationException("Incorrect " + type + " name \"" + name + "\".");
134     }
135     return classes[0];
136   }
137
138   @NotNull
139   @Override
140   public PsiTypeElement createTypeElement(@NotNull PsiType psiType) {
141     LightTypeElement element = new LightTypeElement(myManager, psiType);
142     CodeEditUtil.setNodeGenerated(element.getNode(), true);
143     return element;
144   }
145
146   @NotNull
147   @Override
148   public PsiJavaCodeReferenceElement createReferenceElementByType(@NotNull PsiClassType type) {
149     return type instanceof PsiClassReferenceType
150            ? ((PsiClassReferenceType)type).getReference()
151            : new LightClassTypeReference(myManager, type);
152   }
153
154   @NotNull
155   @Override
156   public PsiTypeParameterList createTypeParameterList() {
157     PsiTypeParameterList parameterList = createMethodFromText("void foo()", null).getTypeParameterList();
158     assert parameterList != null;
159     return parameterList;
160   }
161
162   @NotNull
163   @Override
164   public PsiTypeParameter createTypeParameter(@NotNull String name, @NotNull PsiClassType[] superTypes) {
165     StringBuilder builder = new StringBuilder();
166     builder.append("public <").append(name);
167     if (superTypes.length > 1 || superTypes.length == 1 && !superTypes[0].equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
168       builder.append(" extends ");
169       for (PsiClassType type : superTypes) {
170         if (!type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
171           builder.append(type.getCanonicalText(true)).append('&');
172         }
173       }
174       builder.delete(builder.length() - 1, builder.length());
175     }
176     builder.append("> void foo(){}");
177     try {
178       return createMethodFromText(builder.toString(), null).getTypeParameters()[0];
179     }
180     catch (RuntimeException e) {
181       throw new IncorrectOperationException("type parameter text: " + builder.toString(), (Throwable)e);
182     }
183   }
184
185   @NotNull
186   @Override
187   public PsiField createField(@NotNull String name, @NotNull PsiType type) throws IncorrectOperationException {
188     PsiUtil.checkIsIdentifier(myManager, name);
189     if (PsiType.NULL.equals(type)) {
190       throw new IncorrectOperationException("Cannot create field with type \"null\".");
191     }
192
193     String text = "class _Dummy_ { private " + GenericsUtil.getVariableTypeByExpressionType(type).getCanonicalText(true) + " " + name + "; }";
194     PsiJavaFile aFile = createDummyJavaFile(text);
195     PsiClass[] classes = aFile.getClasses();
196     if (classes.length < 1) {
197       throw new IncorrectOperationException("Class was not created " + text);
198     }
199     PsiClass psiClass = classes[0];
200     PsiField[] fields = psiClass.getFields();
201     if (fields.length < 1) {
202       throw new IncorrectOperationException("Field was not created " + text);
203     }
204     PsiField field = fields[0];
205     field = (PsiField)JavaCodeStyleManager.getInstance(myManager.getProject()).shortenClassReferences(field);
206     return (PsiField)CodeStyleManager.getInstance(myManager.getProject()).reformat(field);
207   }
208
209   @NotNull
210   @Override
211   public PsiMethod createMethod(@NotNull String name, PsiType returnType) throws IncorrectOperationException {
212     PsiUtil.checkIsIdentifier(myManager, name);
213     if (PsiType.NULL.equals(returnType)) {
214       throw new IncorrectOperationException("Cannot create method with type \"null\".");
215     }
216
217     String canonicalText = GenericsUtil.getVariableTypeByExpressionType(returnType).getCanonicalText(true);
218     PsiJavaFile aFile = createDummyJavaFile("class _Dummy_ { public " + canonicalText + " " + name + "() {\n} }");
219     PsiClass[] classes = aFile.getClasses();
220     if (classes.length < 1) {
221       throw new IncorrectOperationException("Class was not created. Method name: " + name + "; return type: " + canonicalText);
222     }
223     PsiMethod[] methods = classes[0].getMethods();
224     if (methods.length < 1) {
225       throw new IncorrectOperationException("Method was not created. Method name: " + name + "; return type: " + canonicalText);
226     }
227     PsiMethod method = methods[0];
228     method = (PsiMethod)JavaCodeStyleManager.getInstance(myManager.getProject()).shortenClassReferences(method);
229     return (PsiMethod)CodeStyleManager.getInstance(myManager.getProject()).reformat(method);
230   }
231
232   @NotNull
233   @Override
234   public PsiMethod createMethod(@NotNull String name, PsiType returnType, PsiElement context) throws IncorrectOperationException {
235     return createMethodFromText("public " + GenericsUtil.getVariableTypeByExpressionType(returnType).getCanonicalText(true) + " " + name + "() {}", context);
236   }
237
238   @NotNull
239   @Override
240   public PsiMethod createConstructor() {
241     return createConstructor("_Dummy_");
242   }
243
244   @NotNull
245   @Override
246   public PsiMethod createConstructor(@NotNull String name) {
247     PsiJavaFile aFile = createDummyJavaFile("class " + name + " { public " + name + "() {} }");
248     PsiMethod method = aFile.getClasses()[0].getMethods()[0];
249     return (PsiMethod)CodeStyleManager.getInstance(myManager.getProject()).reformat(method);
250   }
251
252   @NotNull
253   @Override
254   public PsiMethod createConstructor(@NotNull String name, PsiElement context) {
255     return createMethodFromText(name + "() {}", context);
256   }
257
258   @NotNull
259   @Override
260   public PsiClassInitializer createClassInitializer() throws IncorrectOperationException {
261     PsiJavaFile aFile = createDummyJavaFile("class _Dummy_ { {} }");
262     PsiClassInitializer classInitializer = aFile.getClasses()[0].getInitializers()[0];
263     return (PsiClassInitializer)CodeStyleManager.getInstance(myManager.getProject()).reformat(classInitializer);
264   }
265
266   @NotNull
267   @Override
268   public PsiParameter createParameter(@NotNull String name, @NotNull PsiType type) throws IncorrectOperationException {
269     PsiUtil.checkIsIdentifier(myManager, name);
270     if (PsiType.NULL.equals(type)) {
271       throw new IncorrectOperationException("Cannot create parameter with type \"null\".");
272     }
273
274     String text = type.getCanonicalText(true) + " " + name;
275     PsiParameter parameter = createParameterFromText(text, null);
276     CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(myManager.getProject());
277     PsiUtil.setModifierProperty(parameter, PsiModifier.FINAL,
278                                 JavaCodeStyleSettingsFacade.getInstance(myManager.getProject()).isGenerateFinalParameters());
279     GeneratedMarkerVisitor.markGenerated(parameter);
280     parameter = (PsiParameter)JavaCodeStyleManager.getInstance(myManager.getProject()).shortenClassReferences(parameter);
281     return (PsiParameter)codeStyleManager.reformat(parameter);
282   }
283
284   @Override
285   public PsiParameter createParameter(@NotNull String name, PsiType type, PsiElement context) throws IncorrectOperationException {
286     PsiMethod psiMethod = createMethodFromText("void f(" + type.getCanonicalText(true) + " " + name + ") {}", context);
287     PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
288     return parameters[0];
289   }
290
291   @NotNull
292   @Override
293   public PsiCodeBlock createCodeBlock() {
294     PsiCodeBlock block = createCodeBlockFromText("{}", null);
295     return (PsiCodeBlock)CodeStyleManager.getInstance(myManager.getProject()).reformat(block);
296   }
297
298   @NotNull
299   @Override
300   public PsiClassType createType(@NotNull PsiClass aClass) {
301     return new PsiImmediateClassType(aClass, aClass instanceof PsiTypeParameter ? PsiSubstitutor.EMPTY : createRawSubstitutor(aClass));
302   }
303
304   @NotNull
305   @Override
306   public PsiClassType createType(@NotNull PsiJavaCodeReferenceElement classReference) {
307     return new PsiClassReferenceType(classReference, null);
308   }
309
310   @NotNull
311   @Override
312   public PsiClassType createType(@NotNull PsiClass aClass, PsiType parameter) {
313     PsiTypeParameter[] typeParameters = aClass.getTypeParameters();
314     assert typeParameters.length == 1 : aClass;
315
316     return createType(aClass, PsiSubstitutor.EMPTY.put(typeParameters[0], parameter));
317   }
318
319   @NotNull
320   @Override
321   public PsiClassType createType(@NotNull PsiClass aClass, PsiType... parameters) {
322     return createType(aClass, PsiSubstitutor.EMPTY.putAll(aClass, parameters));
323   }
324
325   @NotNull
326   @Override
327   public PsiSubstitutor createRawSubstitutor(@NotNull PsiTypeParameterListOwner owner) {
328     Map<PsiTypeParameter, PsiType> substitutorMap = null;
329     for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(owner)) {
330       if (substitutorMap == null) substitutorMap = new HashMap<>();
331       substitutorMap.put(parameter, null);
332     }
333     return PsiSubstitutor.createSubstitutor(substitutorMap);
334   }
335
336   @NotNull
337   @Override
338   public PsiSubstitutor createRawSubstitutor(@NotNull PsiSubstitutor baseSubstitutor, @NotNull PsiTypeParameter[] typeParameters) {
339     Map<PsiTypeParameter, PsiType> substitutorMap = null;
340     for (PsiTypeParameter parameter : typeParameters) {
341       if (substitutorMap == null) substitutorMap = new HashMap<>();
342       substitutorMap.put(parameter, null);
343     }
344     return PsiSubstitutor.createSubstitutor(substitutorMap).putAll(baseSubstitutor);
345   }
346
347   @NotNull
348   @Override
349   public PsiElement createDummyHolder(@NotNull String text, @NotNull IElementType type, @Nullable PsiElement context) {
350     DummyHolder result = DummyHolderFactory.createHolder(myManager, context);
351     FileElement holder = result.getTreeElement();
352     Language language = type.getLanguage();
353     ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(language);
354     assert parserDefinition != null : "No parser definition for language " + language;
355     Project project = myManager.getProject();
356     Lexer lexer = parserDefinition.createLexer(project);
357     PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, holder, lexer, language, text);
358     ASTNode node = parserDefinition.createParser(project).parse(type, builder);
359     holder.rawAddChildren((TreeElement)node);
360     PsiElement psi = node.getPsi();
361     assert psi != null : text;
362     return psi;
363   }
364
365   @NotNull
366   @Override
367   public PsiSubstitutor createSubstitutor(@NotNull Map<PsiTypeParameter, PsiType> map) {
368     return PsiSubstitutor.createSubstitutor(map);
369   }
370
371   @Nullable
372   @Override
373   public PsiPrimitiveType createPrimitiveType(@NotNull String text) {
374     return PsiJavaParserFacadeImpl.getPrimitiveType(text);
375   }
376
377   @NotNull
378   @Override
379   public PsiClassType createTypeByFQClassName(@NotNull String qName) {
380     return createTypeByFQClassName(qName, GlobalSearchScope.allScope(myManager.getProject()));
381   }
382
383   @NotNull
384   @Override
385   public PsiClassType createTypeByFQClassName(@NotNull String qName, @NotNull GlobalSearchScope resolveScope) {
386     if (CommonClassNames.JAVA_LANG_OBJECT.equals(qName)) {
387       PsiClassType cachedObjectType = myCachedObjectType.get(resolveScope);
388       if (cachedObjectType != null) {
389         return cachedObjectType;
390       }
391       PsiClass aClass = JavaPsiFacade.getInstance(myManager.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, resolveScope);
392       if (aClass != null) {
393         cachedObjectType = new PsiImmediateClassType(aClass, PsiSubstitutor.EMPTY);
394         cachedObjectType = ConcurrencyUtil.cacheOrGet(myCachedObjectType, resolveScope, cachedObjectType);
395         return cachedObjectType;
396       }
397     }
398     return new PsiClassReferenceType(createReferenceElementByFQClassName(qName, resolveScope), null);
399   }
400
401   @NotNull
402   @Override
403   public PsiJavaCodeReferenceElement createClassReferenceElement(@NotNull PsiClass aClass) {
404     String text;
405     if (aClass instanceof PsiAnonymousClass) {
406       text = ((PsiAnonymousClass)aClass).getBaseClassType().getPresentableText();
407     }
408     else {
409       text = aClass.getName();
410     }
411     if (text == null) {
412       throw new IncorrectOperationException("Invalid class: " + aClass);
413     }
414     return new LightClassReference(myManager, text, aClass);
415   }
416
417   @NotNull
418   @Override
419   public PsiJavaCodeReferenceElement createReferenceElementByFQClassName(@NotNull String qName, @NotNull GlobalSearchScope resolveScope) {
420     String shortName = PsiNameHelper.getShortClassName(qName);
421     return new LightClassReference(myManager, shortName, qName, resolveScope);
422   }
423
424   @NotNull
425   @Override
426   public PsiJavaCodeReferenceElement createFQClassNameReferenceElement(@NotNull String qName, @NotNull GlobalSearchScope resolveScope) {
427     return new LightClassReference(myManager, qName, qName, resolveScope);
428   }
429
430   @NotNull
431   @Override
432   public PsiJavaCodeReferenceElement createPackageReferenceElement(@NotNull PsiPackage aPackage) throws IncorrectOperationException {
433     if (aPackage.getQualifiedName().isEmpty()) {
434       throw new IncorrectOperationException("Cannot create reference to default package.");
435     }
436     return new LightPackageReference(myManager, aPackage);
437   }
438
439   @NotNull
440   @Override
441   public PsiPackageStatement createPackageStatement(@NotNull String name) throws IncorrectOperationException {
442     PsiJavaFile aFile = createDummyJavaFile("package " + name + ";");
443     PsiPackageStatement stmt = aFile.getPackageStatement();
444     if (stmt == null) {
445       throw new IncorrectOperationException("Incorrect package name: " + name);
446     }
447     return stmt;
448   }
449
450   @NotNull
451   @Override
452   public PsiImportStaticStatement createImportStaticStatement(@NotNull PsiClass aClass, @NotNull String memberName) throws IncorrectOperationException {
453     if (aClass instanceof PsiAnonymousClass) {
454       throw new IncorrectOperationException("Cannot create import statement for anonymous class.");
455     }
456     else if (aClass.getParent() instanceof PsiDeclarationStatement) {
457       throw new IncorrectOperationException("Cannot create import statement for local class.");
458     }
459
460     PsiJavaFile aFile = createDummyJavaFile("import static " + aClass.getQualifiedName() + "." + memberName + ";");
461     PsiImportStatementBase statement = extractImport(aFile, true);
462     return (PsiImportStaticStatement)CodeStyleManager.getInstance(myManager.getProject()).reformat(statement);
463   }
464
465   @NotNull
466   @Override
467   public PsiParameterList createParameterList(@NotNull String[] names, @NotNull PsiType[] types) throws IncorrectOperationException {
468     StringBuilder builder = new StringBuilder();
469     builder.append("void method(");
470     for (int i = 0; i < names.length; i++) {
471       if (i > 0) builder.append(", ");
472       builder.append(types[i].getCanonicalText(true)).append(' ').append(names[i]);
473     }
474     builder.append(");");
475     return createMethodFromText(builder.toString(), null).getParameterList();
476   }
477
478   @NotNull
479   @Override
480   public PsiReferenceList createReferenceList(@NotNull PsiJavaCodeReferenceElement[] references) throws IncorrectOperationException {
481     StringBuilder builder = new StringBuilder();
482     builder.append("void method()");
483     if (references.length > 0){
484       builder.append(" throws ");
485       for (int i = 0; i < references.length; i++) {
486         if (i > 0) builder.append(", ");
487         builder.append(references[i].getCanonicalText());
488       }
489     }
490     builder.append(';');
491     return createMethodFromText(builder.toString(), null).getThrowsList();
492   }
493
494   @NotNull
495   @Override
496   public PsiJavaCodeReferenceElement createPackageReferenceElement(@NotNull String packageName) throws IncorrectOperationException {
497     if (packageName.isEmpty()) {
498       throw new IncorrectOperationException("Cannot create reference to default package.");
499     }
500     return new LightPackageReference(myManager, packageName);
501   }
502
503   @NotNull
504   @Override
505   public PsiReferenceExpression createReferenceExpression(@NotNull PsiClass aClass) throws IncorrectOperationException {
506     String text;
507     if (aClass instanceof PsiAnonymousClass) {
508       text = ((PsiAnonymousClass)aClass).getBaseClassType().getPresentableText();
509     }
510     else {
511       text = aClass.getName();
512     }
513     return new LightClassReferenceExpression(myManager, text, aClass);
514   }
515
516   @NotNull
517   @Override
518   public PsiReferenceExpression createReferenceExpression(@NotNull PsiPackage aPackage) throws IncorrectOperationException {
519     if (aPackage.getQualifiedName().isEmpty()) {
520       throw new IncorrectOperationException("Cannot create reference to default package.");
521     }
522     return new LightPackageReferenceExpression(myManager, aPackage);
523   }
524
525   @NotNull
526   @Override
527   public PsiIdentifier createIdentifier(@NotNull String text) throws IncorrectOperationException {
528     PsiUtil.checkIsIdentifier(myManager, text);
529     return new LightIdentifier(myManager, text);
530   }
531
532   @NotNull
533   @Override
534   public PsiKeyword createKeyword(@NotNull String text) throws IncorrectOperationException {
535     if (!PsiNameHelper.getInstance(myManager.getProject()).isKeyword(text)) {
536       throw new IncorrectOperationException("\"" + text + "\" is not a keyword.");
537     }
538     return new LightKeyword(myManager, text);
539   }
540
541   @NotNull
542   @Override
543   public PsiKeyword createKeyword(@NotNull String keyword, PsiElement context) throws IncorrectOperationException {
544     LanguageLevel level = PsiUtil.getLanguageLevel(context);
545     if (!JavaLexer.isKeyword(keyword, level) && !JavaLexer.isSoftKeyword(keyword, level)) {
546       throw new IncorrectOperationException("\"" + keyword + "\" is not a keyword.");
547     }
548     return new LightKeyword(myManager, keyword);
549   }
550
551   @NotNull
552   @Override
553   public PsiImportStatement createImportStatement(@NotNull PsiClass aClass) throws IncorrectOperationException {
554     if (aClass instanceof PsiAnonymousClass) {
555       throw new IncorrectOperationException("Cannot create import statement for anonymous class.");
556     }
557     else if (aClass.getParent() instanceof PsiDeclarationStatement) {
558       throw new IncorrectOperationException("Cannot create import statement for local class.");
559     }
560
561     PsiJavaFile aFile = createDummyJavaFile("import " + aClass.getQualifiedName() + ";");
562     PsiImportStatementBase statement = extractImport(aFile, false);
563     return (PsiImportStatement)CodeStyleManager.getInstance(myManager.getProject()).reformat(statement);
564   }
565
566   @NotNull
567   @Override
568   public PsiImportStatement createImportStatementOnDemand(@NotNull String packageName) throws IncorrectOperationException {
569     if (packageName.isEmpty()) {
570       throw new IncorrectOperationException("Cannot create import statement for default package.");
571     }
572     if (!PsiNameHelper.getInstance(myManager.getProject()).isQualifiedName(packageName)) {
573       throw new IncorrectOperationException("Incorrect package name: \"" + packageName + "\".");
574     }
575
576     PsiJavaFile aFile = createDummyJavaFile("import " + packageName + ".*;");
577     PsiImportStatementBase statement = extractImport(aFile, false);
578     return (PsiImportStatement)CodeStyleManager.getInstance(myManager.getProject()).reformat(statement);
579   }
580
581   @NotNull
582   @Override
583   public PsiDeclarationStatement createVariableDeclarationStatement(@NotNull String name,
584                                                                     @NotNull PsiType type,
585                                                                     @Nullable PsiExpression initializer) throws IncorrectOperationException {
586     return createVariableDeclarationStatement(name, type, initializer, null);
587   }
588
589   @NotNull
590   @Override
591   public PsiDeclarationStatement createVariableDeclarationStatement(@NotNull String name,
592                                                                     @NotNull PsiType type,
593                                                                     @Nullable PsiExpression initializer,
594                                                                     @Nullable PsiElement context) throws IncorrectOperationException {
595     if (!isIdentifier(name)) {
596       throw new IncorrectOperationException("\"" + name + "\" is not an identifier.");
597     }
598     if (PsiType.NULL.equals(type)) {
599       throw new IncorrectOperationException("Cannot create variable with type \"null\".");
600     }
601
602     String text = "X " + name + (initializer != null ? " = x" : "") + ";";
603     PsiDeclarationStatement statement = (PsiDeclarationStatement)createStatementFromText(text, context);
604
605     PsiVariable variable = (PsiVariable)statement.getDeclaredElements()[0];
606     replace(variable.getTypeElement(), createTypeElement(GenericsUtil.getVariableTypeByExpressionType(type)), text);
607
608     boolean generateFinalLocals = JavaCodeStyleSettingsFacade.getInstance(myManager.getProject()).isGenerateFinalLocals();
609     PsiUtil.setModifierProperty(variable, PsiModifier.FINAL, generateFinalLocals);
610
611     if (initializer != null) {
612       replace(variable.getInitializer(), initializer, text);
613     }
614
615     GeneratedMarkerVisitor.markGenerated(statement);
616     return statement;
617   }
618
619   @Override
620   public PsiResourceVariable createResourceVariable(@NotNull String name,
621                                                     @NotNull PsiType type,
622                                                     @Nullable PsiExpression initializer,
623                                                     @Nullable PsiElement context) {
624     PsiTryStatement tryStatement = (PsiTryStatement)createStatementFromText("try (X x = null){}", context);
625     PsiResourceList resourceList = tryStatement.getResourceList();
626     assert resourceList != null;
627     PsiResourceVariable resourceVariable = (PsiResourceVariable)resourceList.iterator().next();
628     resourceVariable.getTypeElement().replace(createTypeElement(type));
629     PsiIdentifier nameIdentifier = resourceVariable.getNameIdentifier();
630     assert nameIdentifier != null;
631     nameIdentifier.replace(createIdentifier(name));
632     if (initializer != null) {
633       resourceVariable.setInitializer(initializer);
634     }
635     return resourceVariable;
636   }
637
638   private static void replace(@Nullable PsiElement original, @NotNull PsiElement replacement, @NotNull String message) {
639     assert original != null : message;
640     original.replace(replacement);
641   }
642
643   @NotNull
644   @Override
645   public PsiDocTag createParamTag(@NotNull String parameterName, String description) throws IncorrectOperationException {
646     StringBuilder builder = new StringBuilder();
647     builder.append(" * @param ");
648     builder.append(parameterName);
649     builder.append(" ");
650     String[] strings = description.split("\\n");
651     for (int i = 0; i < strings.length; i++) {
652       if (i > 0) builder.append("\n * ");
653       builder.append(strings[i]);
654     }
655     return createDocTagFromText(builder.toString());
656   }
657
658   @NotNull
659   @Override
660   public PsiAnnotation createAnnotationFromText(@NotNull String annotationText, @Nullable PsiElement context) throws IncorrectOperationException {
661     PsiAnnotation psiAnnotation = super.createAnnotationFromText(annotationText, context);
662     GeneratedMarkerVisitor.markGenerated(psiAnnotation);
663     return psiAnnotation;
664   }
665
666   public PsiAnnotation createAnnotationFromText(@NotNull String annotationText,
667                                                 @Nullable PsiElement context,
668                                                 boolean markGenerated) throws IncorrectOperationException {
669     PsiAnnotation psiAnnotation = super.createAnnotationFromText(annotationText, context);
670     if (markGenerated) {
671       GeneratedMarkerVisitor.markGenerated(psiAnnotation);
672     }
673     return psiAnnotation;
674   }
675
676   @NotNull
677   @Override
678   public PsiCodeBlock createCodeBlockFromText(@NotNull String text, @Nullable PsiElement context) throws IncorrectOperationException {
679     PsiCodeBlock psiCodeBlock = super.createCodeBlockFromText(text, context);
680     GeneratedMarkerVisitor.markGenerated(psiCodeBlock);
681     return psiCodeBlock;
682   }
683
684   @NotNull
685   @Override
686   public PsiEnumConstant createEnumConstantFromText(@NotNull String text, @Nullable PsiElement context) throws IncorrectOperationException {
687     PsiEnumConstant enumConstant = super.createEnumConstantFromText(text, context);
688     GeneratedMarkerVisitor.markGenerated(enumConstant);
689     return enumConstant;
690   }
691
692   @NotNull
693   @Override
694   public PsiExpression createExpressionFromText(@NotNull String text, @Nullable PsiElement context) throws IncorrectOperationException {
695     PsiExpression expression = super.createExpressionFromText(text, context);
696     GeneratedMarkerVisitor.markGenerated(expression);
697     return expression;
698   }
699
700   @NotNull
701   @Override
702   public PsiField createFieldFromText(@NotNull String text, @Nullable PsiElement context) throws IncorrectOperationException {
703     PsiField psiField = super.createFieldFromText(text, context);
704     GeneratedMarkerVisitor.markGenerated(psiField);
705     return psiField;
706   }
707
708   @NotNull
709   @Override
710   public PsiParameter createParameterFromText(@NotNull String text, @Nullable PsiElement context) throws IncorrectOperationException {
711     PsiParameter parameter = super.createParameterFromText(text, context);
712     GeneratedMarkerVisitor.markGenerated(parameter);
713     return parameter;
714   }
715
716   @NotNull
717   @Override
718   public PsiStatement createStatementFromText(@NotNull String text, @Nullable PsiElement context) throws IncorrectOperationException {
719     PsiStatement statement = super.createStatementFromText(text, context);
720     GeneratedMarkerVisitor.markGenerated(statement);
721     return statement;
722   }
723
724   @NotNull
725   @Override
726   public PsiType createTypeFromText(@NotNull String text, @Nullable PsiElement context) throws IncorrectOperationException {
727     return createTypeInner(text, context, true);
728   }
729
730   @NotNull
731   @Override
732   public PsiTypeParameter createTypeParameterFromText(@NotNull String text, PsiElement context) throws IncorrectOperationException {
733     PsiTypeParameter typeParameter = super.createTypeParameterFromText(text, context);
734     GeneratedMarkerVisitor.markGenerated(typeParameter);
735     return typeParameter;
736   }
737
738   @NotNull
739   @Override
740   public PsiMethod createMethodFromText(@NotNull String text,
741                                         PsiElement context,
742                                         LanguageLevel level) throws IncorrectOperationException {
743     PsiMethod method = super.createMethodFromText(text, context, level);
744     GeneratedMarkerVisitor.markGenerated(method);
745     return method;
746   }
747
748   private static PsiImportStatementBase extractImport(PsiJavaFile aFile, boolean isStatic) {
749     PsiImportList importList = aFile.getImportList();
750     assert importList != null : aFile;
751     PsiImportStatementBase[] statements = isStatic ? importList.getImportStaticStatements() : importList.getImportStatements();
752     assert statements.length == 1 : aFile.getText();
753     return statements[0];
754   }
755
756   private static final JavaParserUtil.ParserWrapper CATCH_SECTION = builder -> JavaParser.INSTANCE.getStatementParser().parseCatchBlock(builder);
757
758   @NotNull
759   @Override
760   public PsiCatchSection createCatchSection(@NotNull PsiType exceptionType,
761                                             @NotNull String exceptionName,
762                                             @Nullable PsiElement context) throws IncorrectOperationException {
763     if (!(exceptionType instanceof PsiClassType || exceptionType instanceof PsiDisjunctionType)) {
764       throw new IncorrectOperationException("Unexpected type:" + exceptionType);
765     }
766
767     String text = "catch (" + exceptionType.getCanonicalText(true) +  " " + exceptionName + ") {}";
768     DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, CATCH_SECTION, level(context)), context);
769     PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
770     if (!(element instanceof PsiCatchSection)) {
771       throw new IncorrectOperationException("Incorrect catch section '" + text + "'. Parsed element: " + element);
772     }
773
774     Project project = myManager.getProject();
775     JavaPsiImplementationHelper helper = JavaPsiImplementationHelper.getInstance(project);
776     helper.setupCatchBlock(exceptionName, exceptionType, context, (PsiCatchSection)element);
777     CodeStyleManager styleManager = CodeStyleManager.getInstance(project);
778     PsiCatchSection catchSection = (PsiCatchSection)styleManager.reformat(element);
779
780     GeneratedMarkerVisitor.markGenerated(catchSection);
781     return catchSection;
782   }
783
784   @Override
785   public boolean isValidClassName(@NotNull String name) {
786     return isIdentifier(name);
787   }
788
789   @Override
790   public boolean isValidMethodName(@NotNull String name) {
791     return isIdentifier(name);
792   }
793
794   @Override
795   public boolean isValidParameterName(@NotNull String name) {
796     return isIdentifier(name);
797   }
798
799   @Override
800   public boolean isValidFieldName(@NotNull String name) {
801     return isIdentifier(name);
802   }
803
804   @Override
805   public boolean isValidLocalVariableName(@NotNull String name) {
806     return isIdentifier(name);
807   }
808
809   private boolean isIdentifier(@NotNull String name) {
810     return PsiNameHelper.getInstance(myManager.getProject()).isIdentifier(name);
811   }
812 }