Files
openide/source/com/intellij/codeInsight/CodeInsightUtil.java
Olesya Smirnova 4d5f207714 Read only handler
2005-01-17 21:17:23 +03:00

412 lines
16 KiB
Java

package com.intellij.codeInsight;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.Indent;
import com.intellij.psi.impl.source.jsp.jspJava.TranslatingChangesDummyHolder;
import com.intellij.psi.jsp.JspFile;
import com.intellij.psi.statistics.StatisticsManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.util.RefactoringMessageUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.text.CharArrayUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
*
*/
public class CodeInsightUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.CodeInsightUtil");
public static PsiExpression findExpressionInRange(PsiFile file, int startOffset, int endOffset) {
PsiElement element1 = file.findElementAt(startOffset);
PsiElement element2 = file.findElementAt(endOffset - 1);
if (element1 instanceof PsiWhiteSpace) {
startOffset = element1.getTextRange().getEndOffset();
element1 = file.findElementAt(startOffset);
}
if (element2 instanceof PsiWhiteSpace) {
endOffset = element2.getTextRange().getStartOffset();
element2 = file.findElementAt(endOffset - 1);
}
if (element1 == null || element2 == null) return null;
PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
while (true) {
if (parent instanceof PsiFile) return null;
if (parent instanceof PsiExpression) {
TextRange range = parent.getTextRange();
if (startOffset != range.getStartOffset() || endOffset != range.getEndOffset()) return null;
if (parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression) return null;
return (PsiExpression) parent;
}
parent = parent.getParent();
}
}
public static PsiElement[] findStatementsInRange(PsiFile file, int startOffset, int endOffset) {
PsiElement element1 = file.findElementAt(startOffset);
PsiElement element2 = file.findElementAt(endOffset - 1);
if (element1 instanceof PsiWhiteSpace) {
startOffset = element1.getTextRange().getEndOffset();
element1 = file.findElementAt(startOffset);
}
if (element2 instanceof PsiWhiteSpace) {
endOffset = element2.getTextRange().getStartOffset();
element2 = file.findElementAt(endOffset - 1);
}
if (element1 == null || element2 == null) return null;
PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
while (true) {
if (parent instanceof PsiStatement) {
parent = parent.getParent();
break;
}
if (parent instanceof PsiCodeBlock) break;
if (parent instanceof JspFile) break;
if (parent instanceof PsiFile) return null;
parent = parent.getParent();
}
while (!element1.getParent().equals(parent)) {
element1 = element1.getParent();
}
if (startOffset != element1.getTextRange().getStartOffset()) return null;
while (!element2.getParent().equals(parent)) {
element2 = element2.getParent();
}
if (endOffset != element2.getTextRange().getEndOffset()) return null;
if (parent instanceof PsiCodeBlock && parent.getParent() instanceof PsiBlockStatement
&& element1 == ((PsiCodeBlock) parent).getLBrace()
&& element2 == ((PsiCodeBlock) parent).getRBrace()) {
return new PsiElement[]{parent.getParent()};
}
/*
if(parent instanceof PsiCodeBlock && parent.getParent() instanceof PsiBlockStatement) {
return new PsiElement[]{parent.getParent()};
}
*/
PsiElement[] children = parent.getChildren();
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
boolean flag = false;
for (int i = 0; i < children.length; i++) {
PsiElement child = children[i];
if (child.equals(element1)) {
flag = true;
}
if (flag && !(child instanceof PsiWhiteSpace)) {
array.add(child);
}
if (child.equals(element2)) {
break;
}
}
for (int i = 0; i < array.size(); i++) {
PsiElement element = array.get(i);
if (!(element instanceof PsiStatement
|| element instanceof PsiWhiteSpace
|| element instanceof PsiComment))
return null;
}
return array.toArray(new PsiElement[array.size()]);
}
public static PsiElement[] getElementsInRange(PsiElement root, final int startOffset, final int endOffset) {
final List<PsiElement> list = new ArrayList<PsiElement>();
// offsets in JspxFile behave themselves bad
if (root instanceof TranslatingChangesDummyHolder) {
root.accept(new PsiRecursiveElementVisitor(){
public void visitElement(PsiElement element) {
list.add(element);
super.visitElement(element);
}
});
return list.toArray(new PsiElement[list.size()]);
}
PsiElement element1 = root.findElementAt(startOffset);
if (element1 == null) element1 = root;
PsiElement element2 = root.findElementAt(endOffset);
if (element2 == null) element2 = root;
PsiElement commonParent = PsiTreeUtil.findCommonParent(element1, element2);
LOG.assertTrue(commonParent != null);
LOG.assertTrue(commonParent.getTextRange() != null);
final int currentOffset = commonParent.getTextRange().getStartOffset();
final PsiElementVisitor visitor = new PsiElementVisitor() {
int offset = currentOffset;
public void visitReferenceExpression(PsiReferenceExpression expression) {
visitElement(expression);
}
public void visitElement(PsiElement element) {
PsiElement child = element.getFirstChild();
if (child == null) {
offset += element.getTextLength();
}
else {
for (; child != null; child = child.getNextSibling()) {
int start = offset;
if (offset > endOffset) break;
child.accept(this);
if (startOffset <= start && offset <= endOffset) {
list.add(child);
}
}
}
}
};
commonParent.accept(visitor);
return list.toArray(new PsiElement[list.size()]);
}
public static void sortIdenticalShortNameClasses(PsiClass[] classes) {
if (classes.length <= 1) return;
final StatisticsManager statisticsManager = StatisticsManager.getInstance();
Comparator<PsiClass> comparator = new Comparator<PsiClass>() {
public int compare(PsiClass aClass, PsiClass bClass) {
int count1 = statisticsManager.getMemberUseCount(null, aClass, null);
int count2 = statisticsManager.getMemberUseCount(null, bClass, null);
if (count1 != count2) return count2 - count1;
boolean inProject1 = aClass.getManager().isInProject(aClass);
boolean inProject2 = bClass.getManager().isInProject(aClass);
if (inProject1 != inProject2) return inProject1 ? -1 : 1;
String qName1 = aClass.getQualifiedName();
boolean isJdk1 = qName1 != null && (qName1.startsWith("java.") || qName1.startsWith("javax."));
String qName2 = bClass.getQualifiedName();
boolean isJdk2 = qName2 != null && (qName2.startsWith("java.") || qName2.startsWith("javax."));
if (isJdk1 != isJdk2) return isJdk1 ? -1 : 1;
return 0;
}
};
Arrays.sort(classes, comparator);
}
public static Indent getMinLineIndent(Project project, Document document, int line1, int line2, FileType fileType) {
CharSequence chars = document.getCharsSequence();
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
Indent minIndent = null;
for (int line = line1; line <= line2; line++) {
int lineStart = document.getLineStartOffset(line);
int textStart = CharArrayUtil.shiftForward(chars, lineStart, " \t");
if (textStart >= document.getTextLength()) {
textStart = document.getTextLength();
}
else {
char c = chars.charAt(textStart);
if (c == '\n' || c == '\r') continue; // empty line
}
String space = chars.subSequence(lineStart, textStart).toString();
Indent indent = codeStyleManager.getIndent(space, fileType);
minIndent = minIndent != null ? indent.min(minIndent) : indent;
}
if (minIndent == null && line1 == line2 && line1 < document.getLineCount() - 1) {
return getMinLineIndent(project, document, line1+1, line1+1, fileType);
}
//if (minIndent == Integer.MAX_VALUE){
// minIndent = 0;
//}
return minIndent;
}
public static String getDefaultValueOfType(PsiType type) {
if (type instanceof PsiArrayType) {
int count = type.getArrayDimensions() - 1;
PsiType componentType = type.getDeepComponentType();
StringBuffer buffer = new StringBuffer();
buffer.append("new ");
buffer.append(componentType.getCanonicalText());
buffer.append("[0]");
for (int i = 0; i < count; i++) {
buffer.append("[]");
}
return buffer.toString();
}
else if (type instanceof PsiPrimitiveType) {
if (PsiType.BOOLEAN == type) {
return "false";
}
else {
return "0";
}
}
else {
return "null";
}
}
public static PsiExpression[] findExpressionOccurrences(PsiElement scope, PsiExpression expr) {
List<PsiExpression> array = new ArrayList<PsiExpression>();
addExpressionOccurrences(RefactoringUtil.unparenthesizeExpression(expr), array, scope);
return array.toArray(new PsiExpression[array.size()]);
}
private static void addExpressionOccurrences(PsiExpression expr, List<PsiExpression> array, PsiElement scope) {
PsiElement[] children = scope.getChildren();
for (int i = 0; i < children.length; i++) {
PsiElement child = children[i];
if (child instanceof PsiExpression) {
if (areExpressionsEquivalent(RefactoringUtil.unparenthesizeExpression((PsiExpression) child), expr)) {
array.add((PsiExpression) child);
continue;
}
}
addExpressionOccurrences(expr, array, child);
}
}
public static PsiExpression[] findReferenceExpressions(PsiElement scope, PsiElement referee) {
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
addReferenceExpressions(array, scope, referee);
return array.toArray(new PsiExpression[array.size()]);
}
private static void addReferenceExpressions(ArrayList<PsiElement> array, PsiElement scope, PsiElement referee) {
PsiElement[] children = scope.getChildren();
for (int i = 0; i < children.length; i++) {
PsiElement child = children[i];
if (child instanceof PsiReferenceExpression) {
PsiElement ref = ((PsiReferenceExpression) child).resolve();
if (ref != null && areElementsEquivalent(ref, referee)) {
array.add(child);
}
}
addReferenceExpressions(array, child, referee);
}
}
public static boolean areExpressionsEquivalent(PsiExpression expr1, PsiExpression expr2) {
if (!areElementsEquivalent(expr1, expr2)) return false;
PsiType type1 = expr1.getType();
PsiType type2 = expr2.getType();
return Comparing.equal(type1, type2);
}
public static boolean areElementsEquivalent(PsiElement element1, PsiElement element2) {
if (!element1.getClass().equals(element2.getClass())) return false; // Q : is it correct to check implementation classes?
PsiElement[] children1 = getFilteredChildren(element1);
PsiElement[] children2 = getFilteredChildren(element2);
if (children1.length != children2.length) return false;
for (int i = 0; i < children1.length; i++) {
PsiElement child1 = children1[i];
PsiElement child2 = children2[i];
if (!areElementsEquivalent(child1, child2)) return false;
}
if (children1.length == 0) {
if (!element1.textMatches(element2)) return false;
}
PsiReference ref1 = element1.getReference();
if (ref1 != null) {
PsiReference ref2 = element2.getReference();
if (ref2 == null) return false;
if (!Comparing.equal(ref1.resolve(), ref2.resolve())) return false;
}
return true;
}
private static PsiElement[] getFilteredChildren(PsiElement element1) {
PsiElement[] children1 = element1.getChildren();
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
for (int i = 0; i < children1.length; i++) {
PsiElement child = children1[i];
if (!(child instanceof PsiWhiteSpace)) {
array.add(child);
}
}
return array.toArray(new PsiElement[array.size()]);
}
public static boolean prepareClassForWrite(final PsiClass aClass) {
PsiFile file = aClass == null ? null : aClass.getContainingFile();
return prepareFileForWrite(file);
}
public static boolean prepareFileForWrite(final PsiFile file) {
if (file == null) {
return false;
}
else if (!file.isWritable()) {
final Project project = file.getProject();
final Editor editor = FileEditorManager.getInstance(project).openTextEditor(
new OpenFileDescriptor(project, file.getVirtualFile()), true);
final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
if (!FileDocumentManager.fileForDocumentCheckedOutSuccessfully(document, project)){
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (editor != null && editor.getComponent().isDisplayable()){
HintManager.getInstance().showErrorHint(
editor,
"File " + file.getVirtualFile().getPresentableUrl() + " is read-only");
}
}
});
}
}
return true;
}
public static Editor positionCursor(final Project project, PsiFile targetFile, PsiElement element) {
TextRange range = element.getTextRange();
int textOffset = range.getStartOffset();
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, targetFile.getVirtualFile(), textOffset);
return FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
}
public static void showReadOnlyFileMessage(Project project, PsiFile file, String message) {
VirtualFile vFile = file.getVirtualFile();
if (vFile.getFileSystem() instanceof JarFileSystem) {
String message1 = message + ".\nFile " + vFile.getPresentableUrl() + " is located in a jar file.";
RefactoringMessageUtil.showErrorMessage("Cannot Modify Jar", message1, null, project);
}
else {
String message1 = message + ".\nFile " + vFile.getPresentableUrl() + " is read-only.";
RefactoringMessageUtil.showErrorMessage("Read-only File", message1, null, project);
final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
if (document != null) {
ApplicationManager.getApplication().invokeLater(new Runnable(){
public void run(){
document.fireReadOnlyModificationAttempt();
}
});
}
}
}
}