Files
openide/source/com/intellij/codeInsight/generation/GenerateMembersUtil.java
Eugene Vigdorchik c5a2e3ee84 (no message)
2005-01-24 14:26:50 +03:00

358 lines
13 KiB
Java

package com.intellij.codeInsight.generation;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class GenerateMembersUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateMembersUtil");
public static Object[] insertMembersAtOffset(Project project,
Document document,
PsiFile file,
int offset,
Object[] memberPrototypes) throws IncorrectOperationException {
PsiElement anchor = findAnchor(file, offset);
if (anchor == null) return null;
PsiClass aClass = (PsiClass) anchor.getParent();
PsiJavaToken lBrace = aClass.getLBrace();
if (lBrace == null) return null;
PsiJavaToken rBrace = aClass.getRBrace();
if (!isChildInRange(anchor, lBrace.getNextSibling(), rBrace)) {
anchor = null;
}
// Q: shouldn't it be somewhere in PSI?
{
PsiElement element = anchor;
while (true) {
if (element == null) break;
if (element instanceof PsiField || element instanceof PsiMethod || element instanceof PsiClassInitializer) break;
element = element.getNextSibling();
}
if (element instanceof PsiField) {
PsiField field = (PsiField) element;
if (!field.getTypeElement().getParent().equals(field)) {
field.normalizeDeclaration();
anchor = field;
}
}
}
if (anchor instanceof PsiWhiteSpace) {
//If anchor was found further due to enum constants
offset = Math.max(anchor.getTextRange().getStartOffset(), offset);
String savedDocumentText = document.getText(); // debug only!
String markerText = "/***/a b;";
document.insertString(offset, markerText);
RangeMarker marker = document.createRangeMarker(offset, offset + markerText.length());
PsiDocumentManager.getInstance(project).commitAllDocuments();
Object[] newMembers = insertMembersAtOffset(project, document, file, offset, memberPrototypes);
SmartPsiElementPointer[] pointers = new SmartPsiElementPointer[newMembers.length];
for (int i = 0; i < newMembers.length; i++) {
Object member = newMembers[i];
PsiElement element;
if (member instanceof PsiElement) {
element = (PsiElement) member;
}
else if (member instanceof TemplateGenerationInfo) {
element = ((TemplateGenerationInfo) member).element;
}
else {
LOG.assertTrue(false);
continue;
}
pointers[i] = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(element);
}
LOG.assertTrue(marker.isValid());
String newMarkerText = document.getCharsSequence().subSequence(marker.getStartOffset(),
marker.getEndOffset()).toString();
LOG.assertTrue(newMarkerText.equals(markerText));
int membersEnd = ((PsiElement) newMembers[newMembers.length - 1]).getTextRange().getEndOffset(); // do-now: templates!!
if (marker.getEndOffset() < membersEnd) {
LOG.error("marker.getEndOffset():" + marker.getEndOffset());
LOG.error("membersEnd:" + membersEnd);
LOG.error("offset:" + offset);
LOG.error(savedDocumentText);
}
document.deleteString(membersEnd, marker.getEndOffset());
PsiDocumentManager.getInstance(project).commitAllDocuments();
PsiElement space = file.findElementAt(membersEnd);
LOG.assertTrue(space instanceof PsiWhiteSpace);
TextRange spaceRange = space.getTextRange();
CodeStyleManager.getInstance(project).reformatRange(file, spaceRange.getStartOffset(), spaceRange.getEndOffset());
for (int i = 0; i < pointers.length; i++) {
SmartPsiElementPointer pointer = pointers[i];
Object member = newMembers[i];
PsiElement element = pointer.getElement();
LOG.assertTrue(element != null);
if (member instanceof PsiElement) {
newMembers[i] = element;
}
else if (member instanceof TemplateGenerationInfo) {
((TemplateGenerationInfo) member).element = element;
}
else {
LOG.assertTrue(false);
continue;
}
}
return newMembers;
}
return insertMembersBeforeAnchor(aClass, anchor, memberPrototypes);
}
private static boolean isChildInRange(PsiElement child, PsiElement first, PsiJavaToken last) {
if (child.equals(first)) return true;
while (true) {
if (child.equals(first)) return false; // before first
if (child.equals(last)) return true;
child = child.getNextSibling();
if (child == null) return false;
}
}
public static Object[] insertMembersBeforeAnchor(PsiClass aClass, PsiElement anchor, Object[] memberPrototypes)
throws IncorrectOperationException {
List<Object> newMembersList = new ArrayList<Object>();
boolean before = true;
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(aClass.getProject());
for (int i = 0; i < memberPrototypes.length; i++) {
if (memberPrototypes[i] instanceof PsiElement) {
PsiElement prototype = (PsiElement) memberPrototypes[i];
PsiElement newMember = insert(aClass, prototype, anchor, before);
newMember = codeStyleManager.shortenClassReferences(newMember);
newMembersList.add(newMember);
anchor = newMember;
before = false;
}
else if (memberPrototypes[i] instanceof TemplateGenerationInfo) {
TemplateGenerationInfo info = (TemplateGenerationInfo) memberPrototypes[i];
info.element = insert(aClass, info.element, anchor, before);
newMembersList.add(info);
anchor = info.element;
before = false;
}
}
return newMembersList.toArray();
}
public static void positionCaret(Editor editor, PsiElement firstMember, boolean toEditBody) {
LOG.assertTrue(firstMember.isValid());
if (toEditBody) {
PsiMethod method = (PsiMethod) firstMember;
PsiCodeBlock body = method.getBody();
if (body != null) {
PsiElement l = body.getLBrace().getNextSibling();
while (l != null && l instanceof PsiWhiteSpace) l = l.getNextSibling();
PsiElement r = body.getRBrace().getPrevSibling();
while (r != null && r instanceof PsiWhiteSpace) r = r.getPrevSibling();
int start = l.getTextRange().getStartOffset(),
end = r.getTextRange().getEndOffset();
editor.getCaretModel().moveToOffset(Math.min(start, end));
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
if (start < end) {
//Not an empty body
editor.getSelectionModel().setSelection(start, end);
}
return;
}
}
int offset;
if (firstMember instanceof PsiMethod) {
PsiMethod method = (PsiMethod) firstMember;
PsiCodeBlock body = method.getBody();
if (body == null) {
offset = method.getTextRange().getStartOffset();
}
else {
offset = body.getLBrace().getTextRange().getEndOffset();
}
}
else {
offset = firstMember.getTextRange().getStartOffset();
}
editor.getCaretModel().moveToOffset(offset);
editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
editor.getSelectionModel().removeSelection();
}
private static PsiElement insert(PsiElement parent, PsiElement element, PsiElement anchor, boolean before)
throws IncorrectOperationException {
if (anchor != null) {
return before ? parent.addBefore(element, anchor) : parent.addAfter(element, anchor);
}
else {
return parent.add(element);
}
/*
try{
if (anchor != null){
return before ? parent.addBefore(element, anchor) : parent.addAfter(element, anchor);
}
else{
return parent.add(element);
}
}
catch(IncorrectOperationException e){
if (anchor != null){
return parent.add(element);
}
else{
throw e;
}
}
*/
}
private static PsiElement findAnchor(PsiFile file, int offset) {
PsiElement element = file.findElementAt(offset);
if (element == null) return null;
while (true) {
if (element instanceof PsiFile) return null;
PsiElement parent = element.getParent();
if (parent instanceof PsiClass && !(parent instanceof PsiTypeParameter)) {
if (((PsiClass)parent).isEnum()) {
PsiElement lastChild = null;
PsiElement[] children = parent.getChildren();
for (int i = 0; i < children.length; i++) {
PsiElement child = children[i];
if (child instanceof PsiJavaToken && ";".equals(child.getText())) {
lastChild = child;
break;
} else if ((child instanceof PsiJavaToken && ",".equals(child.getText())) || child instanceof PsiEnumConstant) {
lastChild = child;
continue;
}
}
if (lastChild != null) {
int adjustedOffset = lastChild.getTextRange().getEndOffset();
if (offset < adjustedOffset) return findAnchor(file, adjustedOffset);
}
}
break;
}
element = parent;
}
return element;
}
public static PsiMethod substituteGenericMethod(PsiMethod method, PsiSubstitutor substitutor) {
Project project = method.getProject();
PsiElementFactory factory = method.getManager().getElementFactory();
PsiMethod newMethod;
PsiTypeParameter[] typeParams = method.getTypeParameterList().getTypeParameters();
try {
PsiType returnType = method.getReturnType();
if (method.isConstructor()) {
newMethod = factory.createConstructor();
newMethod.getNameIdentifier().replace(factory.createIdentifier(method.getName()));
}
else {
newMethod = factory.createMethod(method.getName(), substitutor.substitute(returnType));
}
PsiDocComment docComment = ((PsiMethod)method.getNavigationElement()).getDocComment();
if (docComment != null) {
newMethod.addAfter(docComment, null);
}
PsiParameter[] parameters = method.getParameterList().getParameters();
CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
Map<PsiType,Pair<String,Integer>> m = new HashMap<PsiType, Pair<String,Integer>>();
for (int i = 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
PsiType paramType = substitutor.substitute(parameter.getType());
String paramName = parameter.getName();
if (paramName == null) {
Pair<String, Integer> pair = m.get(paramType);
if (pair != null) {
paramName = pair.first + pair.second;
m.put(paramType, Pair.create(pair.first, new Integer(pair.second.intValue() + 1)));
}
else {
String[] names = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, paramType).names;
if (names.length > 0) {
paramName = names[0];
} else paramName = "p" + i;
m.put(paramType, new Pair<String, Integer>(paramName, new Integer(1)));
}
}
if (paramName == null) paramName = "p" + i;
PsiParameter newParameter = factory.createParameter(paramName, paramType);
newMethod.getParameterList().add(newParameter);
}
for (int i = 0; i < typeParams.length; i++) {
if (substitutor.substitute(typeParams[i]) != null) newMethod.getTypeParameterList().add(typeParams[i]);
}
PsiClassType[] thrownTypes = method.getThrowsList().getReferencedTypes();
for (int i = 0; i < thrownTypes.length; i++) {
PsiType thrownType = substitutor.substitute(thrownTypes[i]);
newMethod.getThrowsList().add(factory.createReferenceElementByType((PsiClassType) thrownType));
}
return newMethod;
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
return method;
}
public static PsiSubstitutor correctSubstitutor(PsiMethod method, PsiSubstitutor substitutor) {
PsiClass hisClass = method.getContainingClass();
PsiTypeParameter[] typeParameters = method.getTypeParameterList().getTypeParameters();
if (typeParameters.length > 0) {
if (PsiUtil.isRawSubstitutor(hisClass, substitutor)) {
for (int j = 0; j < typeParameters.length; j++) {
substitutor = substitutor.put(typeParameters[j], null);
}
}
}
return substitutor;
}
}