| /* |
| * 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 com.intellij.psi.util; |
| |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import org.intellij.lang.annotations.MagicConstant; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| public class PsiFormatUtil extends PsiFormatUtilBase { |
| @MagicConstant(flags = { |
| SHOW_MODIFIERS, SHOW_TYPE, TYPE_AFTER, SHOW_CONTAINING_CLASS, SHOW_FQ_NAME, SHOW_NAME, SHOW_MODIFIERS, |
| SHOW_INITIALIZER, SHOW_RAW_TYPE, SHOW_RAW_NON_TOP_TYPE, SHOW_FQ_CLASS_NAMES}) |
| public @interface FormatVariableOptions { } |
| |
| @MagicConstant(flags = { |
| SHOW_MODIFIERS, MODIFIERS_AFTER, SHOW_TYPE, TYPE_AFTER, SHOW_CONTAINING_CLASS, SHOW_FQ_NAME, SHOW_NAME, |
| SHOW_PARAMETERS, SHOW_THROWS, SHOW_RAW_TYPE, SHOW_RAW_NON_TOP_TYPE, SHOW_FQ_CLASS_NAMES}) |
| public @interface FormatMethodOptions { } |
| |
| @MagicConstant(flags = { |
| SHOW_MODIFIERS, SHOW_NAME, SHOW_ANONYMOUS_CLASS_VERBOSE, SHOW_FQ_NAME, MODIFIERS_AFTER, |
| SHOW_EXTENDS_IMPLEMENTS, SHOW_REDUNDANT_MODIFIERS, JAVADOC_MODIFIERS_ONLY}) |
| public @interface FormatClassOptions { } |
| |
| public static String formatVariable(PsiVariable variable, @FormatVariableOptions int options, PsiSubstitutor substitutor) { |
| StringBuilder buffer = new StringBuilder(); |
| formatVariable(variable, options, substitutor, buffer); |
| return buffer.toString(); |
| } |
| |
| private static void formatVariable(PsiVariable variable, |
| @FormatVariableOptions int options, |
| PsiSubstitutor substitutor, |
| @NotNull StringBuilder buffer) { |
| if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) == 0){ |
| formatModifiers(variable, options,buffer); |
| } |
| if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) == 0){ |
| appendSpaceIfNeeded(buffer); |
| buffer.append(formatType(variable.getType(), options, substitutor)); |
| } |
| if (variable instanceof PsiField && (options & SHOW_CONTAINING_CLASS) != 0){ |
| PsiClass aClass = ((PsiField)variable).getContainingClass(); |
| if (aClass != null){ |
| String className = aClass.getName(); |
| if (className != null) { |
| appendSpaceIfNeeded(buffer); |
| if ((options & SHOW_FQ_NAME) != 0){ |
| String qName = aClass.getQualifiedName(); |
| if (qName != null){ |
| buffer.append(qName); |
| } |
| else{ |
| buffer.append(className); |
| } |
| } |
| else{ |
| buffer.append(className); |
| } |
| buffer.append('.'); |
| } |
| } |
| if ((options & SHOW_NAME) != 0){ |
| buffer.append(variable.getName()); |
| } |
| } |
| else{ |
| if ((options & SHOW_NAME) != 0){ |
| String name = variable.getName(); |
| if (StringUtil.isNotEmpty(name)){ |
| appendSpaceIfNeeded(buffer); |
| buffer.append(name); |
| } |
| } |
| } |
| if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) != 0){ |
| if ((options & SHOW_NAME) != 0 && variable.getName() != null){ |
| buffer.append(':'); |
| } |
| buffer.append(formatType(variable.getType(), options, substitutor)); |
| } |
| if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) != 0){ |
| formatModifiers(variable, options,buffer); |
| } |
| if ((options & SHOW_INITIALIZER) != 0){ |
| PsiExpression initializer = variable.getInitializer(); |
| if (initializer != null){ |
| buffer.append(" = "); |
| String text = PsiExpressionTrimRenderer.render(initializer); |
| int index1 = text.lastIndexOf('\n'); |
| if (index1 < 0) index1 = text.length(); |
| int index2 = text.lastIndexOf('\r'); |
| if (index2 < 0) index2 = text.length(); |
| int index = Math.min(index1, index2); |
| buffer.append(text.substring(0, index)); |
| if (index < text.length()) { |
| buffer.append(" ..."); |
| } |
| } |
| } |
| } |
| |
| public static String formatMethod(PsiMethod method, |
| PsiSubstitutor substitutor, |
| @FormatMethodOptions int options, |
| @FormatVariableOptions int parameterOptions) { |
| return formatMethod(method, substitutor, options, parameterOptions, MAX_PARAMS_TO_SHOW); |
| } |
| |
| public static String formatMethod(PsiMethod method, |
| PsiSubstitutor substitutor, |
| @FormatMethodOptions int options, |
| @FormatVariableOptions int parameterOptions, |
| int maxParametersToShow) { |
| StringBuilder buffer = new StringBuilder(); |
| formatMethod(method, substitutor, options, parameterOptions, maxParametersToShow,buffer); |
| return buffer.toString(); |
| } |
| |
| private static void formatMethod(PsiMethod method, |
| PsiSubstitutor substitutor, |
| @FormatMethodOptions int options, |
| @FormatVariableOptions int parameterOptions, |
| int maxParametersToShow, |
| StringBuilder buffer) { |
| if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) == 0){ |
| formatModifiers(method, options,buffer); |
| } |
| if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) == 0){ |
| PsiType type = method.getReturnType(); |
| if (type != null){ |
| appendSpaceIfNeeded(buffer); |
| buffer.append(formatType(type, options, substitutor)); |
| } |
| } |
| if ((options & SHOW_CONTAINING_CLASS) != 0){ |
| PsiClass aClass = method.getContainingClass(); |
| if (aClass != null){ |
| appendSpaceIfNeeded(buffer); |
| String name = aClass.getName(); |
| if (name != null) { |
| if ((options & SHOW_FQ_NAME) != 0){ |
| String qName = aClass.getQualifiedName(); |
| if (qName != null){ |
| buffer.append(qName); |
| } |
| else{ |
| buffer.append(name); |
| } |
| } |
| else{ |
| buffer.append(name); |
| } |
| buffer.append('.'); |
| } |
| } |
| if ((options & SHOW_NAME) != 0){ |
| buffer.append(method.getName()); |
| } |
| } |
| else{ |
| if ((options & SHOW_NAME) != 0){ |
| appendSpaceIfNeeded(buffer); |
| buffer.append(method.getName()); |
| } |
| } |
| if ((options & SHOW_PARAMETERS) != 0){ |
| buffer.append('('); |
| PsiParameter[] params = method.getParameterList().getParameters(); |
| for(int i = 0; i < Math.min(params.length, maxParametersToShow); i++) { |
| PsiParameter parm = params[i]; |
| if (i > 0){ |
| buffer.append(", "); |
| } |
| buffer.append(formatVariable(parm, parameterOptions, substitutor)); |
| } |
| if(params.length > maxParametersToShow) { |
| buffer.append (", ..."); |
| } |
| buffer.append(')'); |
| } |
| if ((options & SHOW_TYPE) != 0 && (options & TYPE_AFTER) != 0){ |
| PsiType type = method.getReturnType(); |
| if (type != null){ |
| if (buffer.length() > 0){ |
| buffer.append(':'); |
| } |
| buffer.append(formatType(type, options, substitutor)); |
| } |
| } |
| if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) != 0){ |
| formatModifiers(method, options,buffer); |
| } |
| if ((options & SHOW_THROWS) != 0){ |
| String throwsText = formatReferenceList(method.getThrowsList(), options); |
| if (!throwsText.isEmpty()){ |
| appendSpaceIfNeeded(buffer); |
| //noinspection HardCodedStringLiteral |
| buffer.append("throws "); |
| buffer.append(throwsText); |
| } |
| } |
| } |
| |
| @NotNull |
| public static String formatClass(@NotNull PsiClass aClass, @FormatClassOptions int options) { |
| StringBuilder buffer = new StringBuilder(); |
| if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) == 0){ |
| formatModifiers(aClass, options,buffer); |
| } |
| if ((options & SHOW_NAME) != 0){ |
| if (aClass instanceof PsiAnonymousClass && (options & SHOW_ANONYMOUS_CLASS_VERBOSE) != 0) { |
| final PsiClassType baseClassReference = ((PsiAnonymousClass) aClass).getBaseClassType(); |
| PsiClass baseClass = baseClassReference.resolve(); |
| String name = baseClass == null ? baseClassReference.getPresentableText() : formatClass(baseClass, options); |
| buffer.append(PsiBundle.message("anonymous.class.derived.display", name)); |
| } |
| else { |
| String name = aClass.getName(); |
| if (name != null) { |
| appendSpaceIfNeeded(buffer); |
| if ((options & SHOW_FQ_NAME) != 0) { |
| String qName = aClass.getQualifiedName(); |
| if (qName != null) { |
| buffer.append(qName); |
| } |
| else { |
| buffer.append(aClass.getName()); |
| } |
| } |
| else { |
| buffer.append(aClass.getName()); |
| } |
| } |
| } |
| } |
| if ((options & SHOW_MODIFIERS) != 0 && (options & MODIFIERS_AFTER) != 0){ |
| formatModifiers(aClass, options,buffer); |
| } |
| if ((options & SHOW_EXTENDS_IMPLEMENTS) != 0){ |
| String extendsText = formatReferenceList(aClass.getExtendsList(), options); |
| if (!extendsText.isEmpty()){ |
| appendSpaceIfNeeded(buffer); |
| //noinspection HardCodedStringLiteral |
| buffer.append("extends "); |
| buffer.append(extendsText); |
| } |
| String implementsText = formatReferenceList(aClass.getImplementsList(), options); |
| if (!implementsText.isEmpty()){ |
| appendSpaceIfNeeded(buffer); |
| //noinspection HardCodedStringLiteral |
| buffer.append("implements "); |
| buffer.append(implementsText); |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| public static String formatModifiers(PsiElement element, int options) throws IllegalArgumentException { |
| StringBuilder buffer = new StringBuilder(); |
| formatModifiers(element, options, buffer); |
| return buffer.toString(); |
| } |
| |
| private static void formatModifiers(PsiElement element, int options, StringBuilder buffer) throws IllegalArgumentException { |
| PsiModifierList list; |
| boolean isInterface = false; |
| if (element instanceof PsiVariable){ |
| list = ((PsiVariable)element).getModifierList(); |
| } |
| else if (element instanceof PsiMethod){ |
| list = ((PsiMethod)element).getModifierList(); |
| } |
| else if (element instanceof PsiClass){ |
| isInterface = ((PsiClass)element).isInterface(); |
| list = ((PsiClass)element).getModifierList(); |
| if (list == null) return; |
| } |
| else if (element instanceof PsiClassInitializer){ |
| list = ((PsiClassInitializer)element).getModifierList(); |
| if (list == null) return; |
| } |
| else{ |
| throw new IllegalArgumentException(); |
| } |
| if (list == null) return; |
| if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 |
| ? list.hasExplicitModifier(PsiModifier.PUBLIC) |
| : list.hasModifierProperty(PsiModifier.PUBLIC)) { |
| appendModifier(buffer, PsiModifier.PUBLIC); |
| } |
| |
| if (list.hasModifierProperty(PsiModifier.PROTECTED)){ |
| appendModifier(buffer, PsiModifier.PROTECTED); |
| } |
| if (list.hasModifierProperty(PsiModifier.PRIVATE)){ |
| appendModifier(buffer, PsiModifier.PRIVATE); |
| } |
| |
| if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 |
| ? list.hasExplicitModifier(PsiModifier.PACKAGE_LOCAL) |
| : list.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { |
| if (element instanceof PsiClass && element.getParent() instanceof PsiDeclarationStatement) {// local class |
| appendModifier(buffer, PsiBundle.message("local.class.preposition")); |
| } |
| else { |
| appendModifier(buffer, PsiBundle.visibilityPresentation(PsiModifier.PACKAGE_LOCAL)); |
| } |
| } |
| |
| if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 |
| ? list.hasExplicitModifier(PsiModifier.STATIC) |
| : list.hasModifierProperty(PsiModifier.STATIC)) appendModifier(buffer, PsiModifier.STATIC); |
| |
| if (!isInterface && //cls modifier list |
| ((options & SHOW_REDUNDANT_MODIFIERS) == 0 |
| ? list.hasExplicitModifier(PsiModifier.ABSTRACT) |
| : list.hasModifierProperty(PsiModifier.ABSTRACT))) appendModifier(buffer, PsiModifier.ABSTRACT); |
| |
| if ((options & SHOW_REDUNDANT_MODIFIERS) == 0 |
| ? list.hasExplicitModifier(PsiModifier.FINAL) |
| : list.hasModifierProperty(PsiModifier.FINAL)) appendModifier(buffer, PsiModifier.FINAL); |
| |
| if (list.hasModifierProperty(PsiModifier.NATIVE) && (options & JAVADOC_MODIFIERS_ONLY) == 0){ |
| appendModifier(buffer, PsiModifier.NATIVE); |
| } |
| if (list.hasModifierProperty(PsiModifier.SYNCHRONIZED) && (options & JAVADOC_MODIFIERS_ONLY) == 0){ |
| appendModifier(buffer, PsiModifier.SYNCHRONIZED); |
| } |
| if (list.hasModifierProperty(PsiModifier.STRICTFP) && (options & JAVADOC_MODIFIERS_ONLY) == 0){ |
| appendModifier(buffer, PsiModifier.STRICTFP); |
| } |
| if (list.hasModifierProperty(PsiModifier.TRANSIENT) && |
| element instanceof PsiVariable // javac 5 puts transient attr for methods |
| ){ |
| appendModifier(buffer, PsiModifier.TRANSIENT); |
| } |
| if (list.hasModifierProperty(PsiModifier.VOLATILE)){ |
| appendModifier(buffer, PsiModifier.VOLATILE); |
| } |
| } |
| |
| private static void appendModifier(final StringBuilder buffer, final String modifier) { |
| appendSpaceIfNeeded(buffer); |
| buffer.append(modifier); |
| } |
| |
| public static String formatReferenceList(PsiReferenceList list, int options) { |
| StringBuilder buffer = new StringBuilder(); |
| PsiJavaCodeReferenceElement[] refs = list.getReferenceElements(); |
| for(int i = 0; i < refs.length; i++) { |
| PsiJavaCodeReferenceElement ref = refs[i]; |
| if (i > 0){ |
| buffer.append(", "); |
| } |
| buffer.append(formatReference(ref, options)); |
| } |
| return buffer.toString(); |
| } |
| |
| public static String formatType(@Nullable PsiType type, int options, @NotNull PsiSubstitutor substitutor) { |
| type = substitutor.substitute(type); |
| if ((options & SHOW_RAW_TYPE) != 0) { |
| type = TypeConversionUtil.erasure(type); |
| } else if ((options & SHOW_RAW_NON_TOP_TYPE) != 0) { |
| if (!(PsiUtil.resolveClassInType(type) instanceof PsiTypeParameter)) { |
| final boolean preserveEllipsis = type instanceof PsiEllipsisType; |
| type = TypeConversionUtil.erasure(type); |
| if (preserveEllipsis && type instanceof PsiArrayType) { |
| type = new PsiEllipsisType(((PsiArrayType)type).getComponentType()); |
| } |
| } |
| } |
| if (type == null) return "null"; |
| return (options & SHOW_FQ_CLASS_NAMES) == 0 ? type.getPresentableText() : type.getInternalCanonicalText(); |
| } |
| |
| public static String formatReference(PsiJavaCodeReferenceElement ref, int options) { |
| return (options & SHOW_FQ_CLASS_NAMES) == 0 ? ref.getText() : ref.getCanonicalText(); |
| } |
| |
| @Nullable |
| public static String getExternalName(PsiModifierListOwner owner) { |
| return getExternalName(owner, true); |
| } |
| |
| @Nullable |
| public static String getExternalName(PsiModifierListOwner owner, final boolean showParamName) { |
| return getExternalName(owner, showParamName, MAX_PARAMS_TO_SHOW); |
| } |
| |
| @Nullable |
| public static String getExternalName(PsiModifierListOwner owner, final boolean showParamName, int maxParamsToShow) { |
| final StringBuilder builder = new StringBuilder(); |
| if (owner instanceof PsiClass) { |
| ClassUtil.formatClassName((PsiClass)owner, builder); |
| return builder.toString(); |
| } |
| final PsiClass psiClass = PsiTreeUtil.getParentOfType(owner, PsiClass.class, false); |
| if (psiClass == null) return null; |
| ClassUtil.formatClassName(psiClass, builder); |
| if (owner instanceof PsiMethod) { |
| builder.append(" "); |
| formatMethod((PsiMethod)owner, PsiSubstitutor.EMPTY, |
| SHOW_NAME | SHOW_FQ_NAME | SHOW_TYPE | SHOW_PARAMETERS | SHOW_FQ_CLASS_NAMES, |
| showParamName ? SHOW_NAME | SHOW_TYPE | SHOW_FQ_CLASS_NAMES : SHOW_TYPE | SHOW_FQ_CLASS_NAMES, maxParamsToShow, builder); |
| } |
| else if (owner instanceof PsiField) { |
| builder.append(" ").append(((PsiField)owner).getName()); |
| } |
| else if (owner instanceof PsiParameter) { |
| final PsiElement declarationScope = ((PsiParameter)owner).getDeclarationScope(); |
| if (!(declarationScope instanceof PsiMethod)) { |
| return null; |
| } |
| final PsiMethod psiMethod = (PsiMethod)declarationScope; |
| |
| builder.append(" "); |
| formatMethod(psiMethod, PsiSubstitutor.EMPTY, |
| SHOW_NAME | SHOW_FQ_NAME | SHOW_TYPE | SHOW_PARAMETERS | SHOW_FQ_CLASS_NAMES, |
| showParamName ? SHOW_NAME | SHOW_TYPE | SHOW_FQ_CLASS_NAMES : SHOW_TYPE | SHOW_FQ_CLASS_NAMES, maxParamsToShow, builder); |
| builder.append(" "); |
| |
| if (showParamName) { |
| formatVariable((PsiVariable)owner, SHOW_NAME, PsiSubstitutor.EMPTY, builder); |
| } |
| else { |
| builder.append(psiMethod.getParameterList().getParameterIndex((PsiParameter)owner)); |
| } |
| } |
| else { |
| return null; |
| } |
| return builder.toString(); |
| } |
| |
| @Nullable |
| public static String getRawExternalName(PsiModifierListOwner owner) { |
| final StringBuilder builder = new StringBuilder(); |
| final PsiClass psiClass = PsiTreeUtil.getParentOfType(owner, PsiClass.class, false); |
| if (psiClass == null) return null; |
| ClassUtil.formatClassName(psiClass, builder); |
| if (owner instanceof PsiMethod) { |
| builder.append(" "); |
| formatMethod((PsiMethod)owner, PsiSubstitutor.EMPTY, |
| SHOW_NAME | SHOW_FQ_NAME | SHOW_TYPE | SHOW_RAW_TYPE | SHOW_PARAMETERS | SHOW_FQ_CLASS_NAMES, |
| SHOW_TYPE | SHOW_RAW_TYPE | SHOW_FQ_CLASS_NAMES, |
| Integer.MAX_VALUE, builder); |
| } |
| else if (owner instanceof PsiParameter) { |
| final PsiElement declarationScope = ((PsiParameter)owner).getDeclarationScope(); |
| if (!(declarationScope instanceof PsiMethod)) { |
| return null; |
| } |
| final PsiMethod psiMethod = (PsiMethod)declarationScope; |
| |
| builder.append(" "); |
| formatMethod(psiMethod, PsiSubstitutor.EMPTY, |
| SHOW_NAME | SHOW_FQ_NAME | SHOW_TYPE | SHOW_RAW_TYPE | SHOW_PARAMETERS | SHOW_FQ_CLASS_NAMES, |
| SHOW_TYPE | SHOW_RAW_TYPE | SHOW_FQ_CLASS_NAMES, |
| Integer.MAX_VALUE, builder); |
| builder.append(" "); |
| builder.append(psiMethod.getParameterList().getParameterIndex((PsiParameter)owner)); |
| } |
| else { |
| return null; |
| } |
| return builder.toString(); |
| } |
| |
| public static String getPackageDisplayName(@NotNull final PsiClass psiClass) { |
| if (psiClass instanceof PsiTypeParameter) { |
| PsiTypeParameterListOwner owner = ((PsiTypeParameter)psiClass).getOwner(); |
| String ownerName = null; |
| if (owner instanceof PsiClass) { |
| ownerName = ((PsiClass)owner).getQualifiedName(); |
| if (ownerName == null) { |
| ownerName = owner.getName(); |
| } |
| } else if (owner instanceof PsiMethod) { |
| ownerName = owner.getName(); |
| } |
| return ownerName == null ? "type parameter" : "type parameter of " + ownerName; |
| } |
| |
| @NonNls String packageName = psiClass.getQualifiedName(); |
| packageName = packageName == null || packageName.lastIndexOf('.') <= 0 ? "" : packageName.substring(0, packageName.lastIndexOf('.')); |
| if (packageName.isEmpty()) { |
| packageName = "default package"; |
| } |
| return packageName; |
| } |
| } |