blob: 7e2188710361c6f72e932074a2a6327d45296555 [file] [log] [blame]
/*
* Copyright 2000-2014 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 org.jetbrains.plugins.groovy.debugger.fragments;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.file.impl.FileManager;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnAmbiguousClosureContainer;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyFileImpl;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author ven
*/
public class GroovyCodeFragment extends GroovyFileImpl implements JavaCodeFragment, IntentionFilterOwner, GrUnAmbiguousClosureContainer {
private PsiType myThisType;
private PsiType mySuperType;
private ExceptionHandler myExceptionChecker;
private IntentionFilterOwner.IntentionActionsFilter myFilter;
private GlobalSearchScope myResolveScope;
/**
* map from a class's imported name (e.g. its short name or alias) to its qualified name
*/
private final LinkedHashMap<String, GrImportStatement> myPseudoImports = ContainerUtil.newLinkedHashMap();
private final ArrayList<GrImportStatement> myOnDemandImports = ContainerUtil.newArrayList();
private FileViewProvider myViewProvider = null;
public GroovyCodeFragment(Project project, CharSequence text) {
this(project, new LightVirtualFile("Dummy.groovy", GroovyFileType.GROOVY_FILE_TYPE, text));
}
public GroovyCodeFragment(Project project, VirtualFile virtualFile) {
super(new SingleRootFileViewProvider(PsiManager.getInstance(project), virtualFile, true));
((SingleRootFileViewProvider)getViewProvider()).forceCachedPsi(this);
}
@Override
public void setThisType(PsiType thisType) {
myThisType = thisType;
}
@Override
public PsiType getSuperType() {
return mySuperType;
}
@Override
public void setSuperType(PsiType superType) {
mySuperType = superType;
}
@Override
@NotNull
public FileViewProvider getViewProvider() {
if (myViewProvider != null) return myViewProvider;
return super.getViewProvider();
}
@Override
public boolean isValid() {
if (!super.isValid()) return false;
PsiElement context = getContext();
return context == null || context.isValid();
}
/**
* @return list of imports in format "qname[:imported_name](,qname[:imported_name])*"
*/
@Override
public String importsToString() {
if (myPseudoImports.isEmpty()) return "";
StringBuilder buffer = new StringBuilder();
for (Map.Entry<String, GrImportStatement> entry : myPseudoImports.entrySet()) {
final String importedName = entry.getKey();
final GrImportStatement anImport = entry.getValue();
//buffer.append(anImport.isStatic() ? "+" : "-");
final String qname = anImport.getImportReference().getClassNameText();
buffer.append(qname);
buffer.append(':').append(importedName);
buffer.append(',');
}
for (GrImportStatement anImport : myOnDemandImports) {
//buffer.append(anImport.isStatic() ? "+" : "-");
String packName = anImport.getImportReference().getClassNameText();
buffer.append(packName);
buffer.append(',');
}
buffer.deleteCharAt(buffer.length() - 1);
return buffer.toString();
}
@Override
public void addImportsFromString(String imports) {
for (String anImport : imports.split(",")) {
int colon = anImport.indexOf(':');
if (colon >= 0) {
String qname = anImport.substring(0, colon);
String importedName = anImport.substring(colon + 1);
myPseudoImports.put(importedName, createSingleImport(qname, importedName));
}
else {
myOnDemandImports.add(createImportOnDemand(anImport));
}
}
}
@Override
public void setVisibilityChecker(JavaCodeFragment.VisibilityChecker checker) {
}
@Override
public VisibilityChecker getVisibilityChecker() {
return VisibilityChecker.EVERYTHING_VISIBLE;
}
@Override
public void setExceptionHandler(ExceptionHandler checker) {
myExceptionChecker = checker;
}
@Override
public ExceptionHandler getExceptionHandler() {
return myExceptionChecker;
}
@Override
public void setIntentionActionsFilter(@NotNull IntentionActionsFilter filter) {
myFilter = filter;
}
@Override
public IntentionActionsFilter getIntentionActionsFilter() {
return myFilter;
}
@Override
public void forceResolveScope(GlobalSearchScope scope) {
myResolveScope = scope;
}
@Override
public GlobalSearchScope getForcedResolveScope() {
return myResolveScope;
}
@Override
public boolean importClass(PsiClass aClass) {
return false;
}
@Override
public PsiType getThisType() {
return myThisType;
}
@Override
protected boolean processImports(PsiScopeProcessor processor,
@NotNull ResolveState state,
PsiElement lastParent,
@NotNull PsiElement place,
@NotNull GrImportStatement[] importStatements,
boolean onDemand) {
if (!super.processImports(processor, state, lastParent, place, importStatements, onDemand)) {
return false;
}
if (!processPseudoImports(processor, state, lastParent, place, onDemand)) {
return false;
}
return true;
}
@Override
protected GroovyCodeFragment clone() {
final GroovyCodeFragment clone = (GroovyCodeFragment)cloneImpl((FileElement)calcTreeElement().clone());
clone.myOriginalFile = this;
clone.myPseudoImports.putAll(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;
}
protected boolean processPseudoImports(PsiScopeProcessor processor,
@NotNull ResolveState state,
PsiElement lastParent,
PsiElement place,
boolean onDemand) {
if (onDemand) {
if (!processImportsOnDemand(processor, state, lastParent, place)) {
return false;
}
}
else {
if (!processSingleImports(processor, state, lastParent, place)) {
return false;
}
}
return true;
}
private boolean processImportsOnDemand(PsiScopeProcessor processor, ResolveState state, PsiElement parent, PsiElement place) {
for (GrImportStatement anImport : myOnDemandImports) {
if (!anImport.processDeclarations(processor, state, parent, place)) {
return false;
}
}
return true;
}
private boolean processSingleImports(PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, PsiElement place) {
NameHint nameHint = processor.getHint(NameHint.KEY);
String name = nameHint != null ? nameHint.getName(state) : null;
if (name != null) {
final GrImportStatement anImport = myPseudoImports.get(name);
if (anImport != null) {
if (!anImport.processDeclarations(processor, state, lastParent, place)) {
return false;
}
}
}
else {
for (GrImportStatement anImport : myPseudoImports.values()) {
if (!anImport.processDeclarations(processor, state, lastParent, place)) {
return false;
}
}
}
return true;
}
@Nullable
private GrImportStatement createImportOnDemand(@NotNull String qname) {
final PsiClass aClass = JavaPsiFacade.getInstance(getProject()).findClass(qname, getResolveScope());
final boolean isStatic = aClass != null;
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(getProject());
try {
return factory.createImportStatement(qname, isStatic, true, null, this);
}
catch (IncorrectOperationException e) {
return null;
}
}
@Nullable
private GrImportStatement createSingleImport(@NotNull String qname, @Nullable String importedName) {
final PsiClass aClass = JavaPsiFacade.getInstance(getProject()).findClass(qname, getResolveScope());
final boolean isStatic = aClass == null;
final String className = PsiNameHelper.getShortClassName(qname);
final String alias = importedName == null || className.equals(importedName) ? null : importedName;
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(getProject());
try {
return factory.createImportStatement(qname, isStatic, false, alias, this);
}
catch (IncorrectOperationException e) {
return null;
}
}
public void clearImports() {
myPseudoImports.clear();
myOnDemandImports.clear();
}
}