| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.psi.impl.source; |
| |
| import com.intellij.lang.Language; |
| import com.intellij.openapi.command.undo.BasicUndoableAction; |
| import com.intellij.openapi.command.undo.UndoManager; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.fileTypes.FileType; |
| import com.intellij.openapi.fileTypes.FileTypeManager; |
| import com.intellij.openapi.fileTypes.StdFileTypes; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.PsiManagerEx; |
| import com.intellij.psi.impl.file.impl.FileManager; |
| import com.intellij.psi.impl.source.resolve.JavaResolveUtil; |
| import com.intellij.psi.impl.source.tree.FileElement; |
| import com.intellij.psi.impl.source.tree.JavaElementType; |
| import com.intellij.psi.scope.ElementClassHint; |
| import com.intellij.psi.scope.NameHint; |
| import com.intellij.psi.scope.PsiScopeProcessor; |
| import com.intellij.psi.scope.util.PsiScopesUtil; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.testFramework.LightVirtualFile; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.LinkedHashMap; |
| import java.util.StringTokenizer; |
| |
| public class PsiCodeFragmentImpl extends PsiFileImpl implements JavaCodeFragment, IntentionFilterOwner { |
| private final PsiElement myContext; |
| private boolean myPhysical; |
| private PsiType myThisType; |
| private PsiType mySuperType; |
| private LinkedHashMap<String, String> myPseudoImports = new LinkedHashMap<String, String>(); |
| private VisibilityChecker myVisibilityChecker; |
| private ExceptionHandler myExceptionHandler; |
| private GlobalSearchScope myResolveScope; |
| private IntentionActionsFilter myIntentionActionsFilter; |
| |
| public PsiCodeFragmentImpl(Project project, |
| IElementType contentElementType, |
| boolean isPhysical, |
| @NonNls String name, |
| CharSequence text, |
| @Nullable PsiElement context) { |
| super(TokenType.CODE_FRAGMENT, |
| contentElementType, |
| ((PsiManagerEx)PsiManager.getInstance(project)).getFileManager().createFileViewProvider( |
| new LightVirtualFile(name, FileTypeManager.getInstance().getFileTypeByFileName(name), text), isPhysical) |
| ); |
| myContext = context; |
| ((SingleRootFileViewProvider)getViewProvider()).forceCachedPsi(this); |
| myPhysical = isPhysical; |
| } |
| |
| @Override |
| @NotNull |
| public Language getLanguage() { |
| return getContentElementType().getLanguage(); |
| } |
| |
| @Override |
| protected PsiCodeFragmentImpl clone() { |
| final PsiCodeFragmentImpl clone = (PsiCodeFragmentImpl)cloneImpl((FileElement)calcTreeElement().clone()); |
| clone.myPhysical = false; |
| clone.myOriginalFile = this; |
| clone.myPseudoImports = new LinkedHashMap<String, String>(myPseudoImports); |
| FileManager fileManager = ((PsiManagerEx)getManager()).getFileManager(); |
| SingleRootFileViewProvider cloneViewProvider = (SingleRootFileViewProvider)fileManager.createFileViewProvider(new LightVirtualFile( |
| getName(), |
| getLanguage(), |
| getText()), false); |
| cloneViewProvider.forceCachedPsi(clone); |
| clone.myViewProvider = cloneViewProvider; |
| return clone; |
| } |
| |
| private FileViewProvider myViewProvider = null; |
| |
| @Override |
| @NotNull |
| public FileViewProvider getViewProvider() { |
| if (myViewProvider != null) return myViewProvider; |
| return super.getViewProvider(); |
| } |
| |
| @Override |
| public boolean isValid() { |
| if (!super.isValid()) return false; |
| return myContext == null || myContext.isValid(); |
| } |
| |
| @Override |
| @NotNull |
| public FileType getFileType() { |
| return StdFileTypes.JAVA; |
| } |
| |
| @Override |
| public PsiElement getContext() { |
| return myContext; |
| } |
| |
| @Override |
| public PsiType getThisType() { |
| return myThisType; |
| } |
| |
| @Override |
| public void setThisType(PsiType psiType) { |
| myThisType = psiType; |
| } |
| |
| @Override |
| public PsiType getSuperType() { |
| return mySuperType; |
| } |
| |
| @Override |
| public void setSuperType(final PsiType superType) { |
| mySuperType = superType; |
| } |
| |
| @Override |
| public String importsToString() { |
| return StringUtil.join(myPseudoImports.values(), ","); |
| } |
| |
| @Override |
| public void addImportsFromString(String imports) { |
| StringTokenizer tokenizer = new StringTokenizer(imports, ","); |
| while (tokenizer.hasMoreTokens()) { |
| String qName = tokenizer.nextToken(); |
| String name = PsiNameHelper.getShortClassName(qName); |
| myPseudoImports.put(name, qName); |
| } |
| } |
| |
| @Override |
| public void setVisibilityChecker(VisibilityChecker checker) { |
| myVisibilityChecker = checker; |
| } |
| |
| @Override |
| public VisibilityChecker getVisibilityChecker() { |
| return myVisibilityChecker; |
| } |
| |
| @Override |
| public boolean isPhysical() { |
| return myPhysical; |
| } |
| |
| @Override |
| public void accept(@NotNull PsiElementVisitor visitor) { |
| if (visitor instanceof JavaElementVisitor) { |
| ((JavaElementVisitor)visitor).visitCodeFragment(this); |
| } |
| else { |
| visitor.visitFile(this); |
| } |
| } |
| |
| @Override |
| public boolean processDeclarations(@NotNull PsiScopeProcessor processor, |
| @NotNull ResolveState state, |
| PsiElement lastParent, |
| @NotNull PsiElement place) { |
| final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY); |
| |
| if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) { |
| final NameHint nameHint = processor.getHint(NameHint.KEY); |
| final String name = nameHint != null ? nameHint.getName(state) : null; |
| if (name != null) { |
| String qNameImported = myPseudoImports.get(name); |
| if (qNameImported != null) { |
| PsiClass imported = JavaPsiFacade.getInstance(myManager.getProject()).findClass(qNameImported, getResolveScope()); |
| if (imported != null) { |
| if (!processor.execute(imported, state)) return false; |
| } |
| } |
| } |
| else { |
| for (String qNameImported : myPseudoImports.values()) { |
| PsiClass aClass = JavaPsiFacade.getInstance(myManager.getProject()).findClass(qNameImported, getResolveScope()); |
| if (aClass != null) { |
| if (!processor.execute(aClass, state)) return false; |
| } |
| } |
| } |
| |
| if (myContext == null) { |
| return JavaResolveUtil.processImplicitlyImportedPackages(processor, state, place, getManager()); |
| } |
| } |
| |
| IElementType i = myContentElementType; |
| if (i == JavaElementType.TYPE_TEXT || i == JavaElementType.EXPRESSION_STATEMENT || i == JavaElementType.REFERENCE_TEXT) { |
| return true; |
| } |
| else { |
| processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this); |
| if (lastParent == null) { |
| // Parent element should not see our vars |
| return true; |
| } |
| |
| return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place); |
| } |
| } |
| |
| public String toString() { |
| return "PsiCodeFragment:" + getName(); |
| } |
| |
| @Override |
| public boolean importClass(PsiClass aClass) { |
| final String className = aClass.getName(); |
| final String qName = aClass.getQualifiedName(); |
| if (qName == null) return false; |
| //if (!myPseudoImports.containsKey(className)){ |
| myPseudoImports.put(className, qName); |
| myManager.beforeChange(false); // to clear resolve caches! |
| if (isPhysical()) { |
| final Project project = myManager.getProject(); |
| final Document document = PsiDocumentManager.getInstance(project).getDocument(this); |
| UndoManager.getInstance(project).undoableActionPerformed(new ImportClassUndoableAction(className, qName, document, myPseudoImports)); |
| } |
| return true; |
| //} |
| //else{ |
| // return false; |
| //} |
| } |
| |
| private static class ImportClassUndoableAction extends BasicUndoableAction { |
| private final String myClassName; |
| private final String myQName; |
| private final LinkedHashMap<String, String> myPseudoImports; |
| |
| public ImportClassUndoableAction(final String className, |
| final String qName, |
| final Document document, |
| final LinkedHashMap<String, String> pseudoImportsMap) { |
| super(document); |
| myClassName = className; |
| myQName = qName; |
| myPseudoImports = pseudoImportsMap; |
| } |
| |
| @Override |
| public void undo() { |
| myPseudoImports.remove(myClassName); |
| } |
| |
| @Override |
| public void redo() { |
| myPseudoImports.put(myClassName, myQName); |
| } |
| } |
| |
| @Override |
| public ExceptionHandler getExceptionHandler() { |
| return myExceptionHandler; |
| } |
| |
| @Override |
| public void setIntentionActionsFilter(@NotNull final IntentionActionsFilter filter) { |
| myIntentionActionsFilter = filter; |
| } |
| |
| @Override |
| public IntentionActionsFilter getIntentionActionsFilter() { |
| return myIntentionActionsFilter; |
| } |
| |
| @Override |
| public void forceResolveScope(GlobalSearchScope scope) { |
| myResolveScope = scope; |
| } |
| |
| @Override |
| public GlobalSearchScope getForcedResolveScope() { |
| return myResolveScope; |
| } |
| |
| @Override |
| @NotNull |
| public GlobalSearchScope getResolveScope() { |
| if (myResolveScope != null) return myResolveScope; |
| return super.getResolveScope(); |
| } |
| |
| @Override |
| public void setExceptionHandler(final ExceptionHandler exceptionHandler) { |
| myExceptionHandler = exceptionHandler; |
| } |
| } |