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