001    /*
002     * Copyright 2010-2014 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.renderer;
018    
019    import kotlin.Function1;
020    import kotlin.KotlinPackage;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
025    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026    import org.jetbrains.jet.lang.descriptors.annotations.DefaultAnnotationArgumentVisitor;
027    import org.jetbrains.jet.lang.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies;
028    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
029    import org.jetbrains.jet.lang.resolve.constants.AnnotationValue;
030    import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
031    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
032    import org.jetbrains.jet.lang.resolve.constants.JavaClassValue;
033    import org.jetbrains.jet.lang.resolve.name.FqName;
034    import org.jetbrains.jet.lang.resolve.name.FqNameBase;
035    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
036    import org.jetbrains.jet.lang.resolve.name.Name;
037    import org.jetbrains.jet.lang.types.*;
038    import org.jetbrains.jet.lang.types.ErrorUtils.UninferredParameterTypeConstructor;
039    import org.jetbrains.jet.lang.types.error.MissingDependencyErrorClass;
040    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
041    import org.jetbrains.jet.utils.UtilsPackage;
042    
043    import java.util.*;
044    
045    import static org.jetbrains.jet.lang.types.TypeUtils.CANT_INFER_LAMBDA_PARAM_TYPE;
046    import static org.jetbrains.jet.lang.types.TypeUtils.DONT_CARE;
047    
048    public class DescriptorRendererImpl implements DescriptorRenderer {
049    
050        private final Function1<JetType, JetType> typeNormalizer;
051        private final boolean shortNames;
052        private final boolean withDefinedIn;
053        private final Set<DescriptorRenderer.Modifier> modifiers;
054        private final boolean startFromName;
055        private final boolean debugMode;
056        private final boolean classWithPrimaryConstructor;
057        private final boolean verbose;
058        private final boolean unitReturnType;
059        private final boolean normalizedVisibilities;
060        private final boolean showInternalKeyword;
061        private final boolean prettyFunctionTypes;
062        private final boolean uninferredTypeParameterAsName;
063        private final boolean includeSynthesizedParameterNames;
064        private final boolean withoutFunctionParameterNames;
065        private final boolean withoutTypeParameters;
066        private final boolean renderClassObjectName;
067        private final boolean withoutSuperTypes;
068        private final boolean receiverAfterName;
069        private final boolean renderDefaultValues;
070        private final boolean flexibleTypesForCode;
071    
072        @NotNull
073        private final OverrideRenderingPolicy overrideRenderingPolicy;
074        @NotNull
075        private final ValueParametersHandler handler;
076        @NotNull
077        private final TextFormat textFormat;
078        private final boolean includePropertyConstant;
079        @NotNull
080        private final Set<FqName> excludedAnnotationClasses;
081    
082        /* package */ DescriptorRendererImpl(
083                boolean shortNames,
084                boolean withDefinedIn,
085                Set<Modifier> modifiers,
086                boolean startFromName,
087                boolean debugMode,
088                boolean classWithPrimaryConstructor,
089                boolean verbose,
090                boolean unitReturnType,
091                boolean normalizedVisibilities,
092                boolean showInternalKeyword,
093                boolean prettyFunctionTypes,
094                boolean uninferredTypeParameterAsName,
095                @NotNull OverrideRenderingPolicy overrideRenderingPolicy,
096                @NotNull ValueParametersHandler handler,
097                @NotNull TextFormat textFormat,
098                @NotNull Collection<FqName> excludedAnnotationClasses,
099                boolean includePropertyConstant,
100                boolean includeSynthesizedParameterNames,
101                boolean withoutFunctionParameterNames,
102                boolean withoutTypeParameters,
103                boolean receiverAfterName,
104                boolean renderClassObjectName,
105                boolean withoutSuperTypes,
106                @NotNull Function1<JetType, JetType> typeNormalizer,
107                boolean renderDefaultValues,
108                boolean flexibleTypesForCode
109        ) {
110            this.shortNames = shortNames;
111            this.withDefinedIn = withDefinedIn;
112            this.modifiers = modifiers;
113            this.startFromName = startFromName;
114            this.handler = handler;
115            this.classWithPrimaryConstructor = classWithPrimaryConstructor;
116            this.verbose = verbose;
117            this.unitReturnType = unitReturnType;
118            this.normalizedVisibilities = normalizedVisibilities;
119            this.showInternalKeyword = showInternalKeyword;
120            this.overrideRenderingPolicy = overrideRenderingPolicy;
121            this.debugMode = debugMode;
122            this.textFormat = textFormat;
123            this.includePropertyConstant = includePropertyConstant;
124            this.excludedAnnotationClasses = new HashSet<FqName>(excludedAnnotationClasses);
125            this.prettyFunctionTypes = prettyFunctionTypes;
126            this.uninferredTypeParameterAsName = uninferredTypeParameterAsName;
127            this.includeSynthesizedParameterNames = includeSynthesizedParameterNames;
128            this.withoutFunctionParameterNames = withoutFunctionParameterNames;
129            this.withoutTypeParameters = withoutTypeParameters;
130            this.receiverAfterName = receiverAfterName;
131            this.renderClassObjectName = renderClassObjectName;
132            this.withoutSuperTypes = withoutSuperTypes;
133            this.typeNormalizer = typeNormalizer;
134            this.renderDefaultValues = renderDefaultValues;
135            this.flexibleTypesForCode = flexibleTypesForCode;
136        }
137    
138        /* FORMATTING */
139        @NotNull
140        private String renderKeyword(@NotNull String keyword) {
141            switch (textFormat) {
142                case PLAIN:
143                    return keyword;
144                case HTML:
145                    return "<b>" + keyword + "</b>";
146            }
147            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
148        }
149    
150        @NotNull
151        private String renderError(@NotNull String keyword) {
152            switch (textFormat) {
153                case PLAIN:
154                    return keyword;
155                case HTML:
156                    return "<font color=red><b>" + keyword + "</b></font>";
157            }
158            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
159        }
160    
161        @NotNull
162        private String escape(@NotNull String string) {
163            switch (textFormat) {
164                case PLAIN:
165                    return string;
166                case HTML:
167                    return string.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
168            }
169            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
170        }
171    
172        @NotNull
173        private String lt() {
174            return escape("<");
175        }
176    
177        @NotNull
178        private String gt() {
179            return escape(">");
180        }
181    
182        @NotNull
183        private String arrow() {
184            switch (textFormat) {
185                case PLAIN:
186                    return escape("->");
187                case HTML:
188                    return "&rarr;";
189            }
190            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
191        }
192    
193        @NotNull
194        private String renderMessage(@NotNull String message) {
195            switch (textFormat) {
196                case PLAIN:
197                    return message;
198                case HTML:
199                    return "<i>" + message + "</i>";
200            }
201            throw new IllegalStateException("Unexpected textFormat: " + textFormat);
202        }
203    
204        private static void renderSpaceIfNeeded(@NotNull StringBuilder builder) {
205            int length = builder.length();
206            if (length == 0 || builder.charAt(length - 1) != ' ') {
207                builder.append(' ');
208            }
209        }
210    
211        /* NAMES RENDERING */
212        @Override
213        @NotNull
214        public String renderName(@NotNull Name identifier) {
215            String asString = identifier.asString();
216            return escape(nameShouldBeEscaped(identifier) ? '`' + asString + '`' : asString);
217        }
218    
219        private static boolean nameShouldBeEscaped(@NotNull Name identifier) {
220            if (identifier.isSpecial()) return false;
221    
222            String name = identifier.asString();
223    
224            if (KeywordStringsGenerated.KEYWORDS.contains(name)) return true;
225    
226            for (int i = 0; i < name.length(); i++) {
227                char c = name.charAt(i);
228                if (!Character.isLetterOrDigit(c) && c != '_') return true;
229            }
230    
231            return false;
232        }
233    
234        private void renderName(@NotNull DeclarationDescriptor descriptor, @NotNull StringBuilder builder) {
235            builder.append(renderName(descriptor.getName()));
236        }
237    
238        private void renderClassObjectName(@NotNull DeclarationDescriptor descriptor, @NotNull StringBuilder builder) {
239            if (renderClassObjectName) {
240                if (startFromName) {
241                    builder.append("class object");
242                }
243                renderSpaceIfNeeded(builder);
244                DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
245                if (containingDeclaration != null) {
246                    builder.append("of ");
247                    builder.append(renderName(containingDeclaration.getName()));
248                }
249            }
250            if (verbose) {
251                if (!startFromName) renderSpaceIfNeeded(builder);
252                builder.append(renderName(descriptor.getName()));
253            }
254        }
255    
256        @Override
257        @NotNull
258        public String renderFqName(@NotNull FqNameBase fqName) {
259            return renderFqName(fqName.pathSegments());
260        }
261    
262    
263        @NotNull
264        private String renderFqName(@NotNull List<Name> pathSegments) {
265            StringBuilder buf = new StringBuilder();
266            for (Name element : pathSegments) {
267                if (buf.length() != 0) {
268                    buf.append(".");
269                }
270                buf.append(renderName(element));
271            }
272            return buf.toString();
273        }
274    
275        @NotNull
276        private String renderClassName(@NotNull ClassDescriptor klass) {
277            if (klass instanceof MissingDependencyErrorClass) {
278                return ((MissingDependencyErrorClass) klass).getFullFqName().asString();
279            }
280            if (ErrorUtils.isError(klass)) {
281                return klass.getTypeConstructor().toString();
282            }
283            if (shortNames) {
284                List<Name> qualifiedNameElements = new ArrayList<Name>();
285    
286                // for nested classes qualified name should be used
287                DeclarationDescriptor current = klass;
288                do {
289                    if (((ClassDescriptor) current).getKind() != ClassKind.CLASS_OBJECT) {
290                        qualifiedNameElements.add(current.getName());
291                    }
292                    current = current.getContainingDeclaration();
293                }
294                while (current instanceof ClassDescriptor);
295    
296                Collections.reverse(qualifiedNameElements);
297                return renderFqName(qualifiedNameElements);
298            }
299            return renderFqName(DescriptorUtils.getFqName(klass));
300        }
301    
302        /* TYPES RENDERING */
303        @NotNull
304        @Override
305        public String renderType(@NotNull JetType type) {
306            return renderNormalizedType(typeNormalizer.invoke(type));
307        }
308    
309        @NotNull
310        private String renderNormalizedType(@NotNull JetType type) {
311            if (TypesPackage.isFlexible(type)) {
312                if (debugMode) {
313                    return renderFlexibleTypeWithBothBounds(TypesPackage.flexibility(type).getLowerBound(),
314                                                            TypesPackage.flexibility(type).getUpperBound());
315                }
316                else if (flexibleTypesForCode) {
317                    String prefix = shortNames ? "" : Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getPackageFqName().asString() + ".";
318                    return prefix + Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getRelativeClassName()
319                           + lt()
320                           + renderNormalizedType(TypesPackage.flexibility(type).getLowerBound()) + ", "
321                           + renderNormalizedType(TypesPackage.flexibility(type).getUpperBound())
322                           + gt();
323                }
324                else {
325                    return renderFlexibleType(type);
326                }
327            }
328            return renderInflexibleType(type);
329        }
330    
331        private String renderFlexibleTypeWithBothBounds(@NotNull JetType lower, @NotNull JetType upper) {
332            return "(" + renderNormalizedType(lower) + ".." + renderNormalizedType(upper) + ")";
333        }
334    
335        private String renderInflexibleType(@NotNull JetType type) {
336            assert !TypesPackage.isFlexible(type) : "Flexible types not allowed here: " + renderNormalizedType(type);
337    
338            if (type == CANT_INFER_LAMBDA_PARAM_TYPE || type == DONT_CARE) {
339                return "???";
340            }
341            if (ErrorUtils.isUninferredParameter(type)) {
342                if (uninferredTypeParameterAsName) {
343                    return renderError(((UninferredParameterTypeConstructor) type.getConstructor()).getTypeParameterDescriptor().getName().toString());
344                }
345                return "???";
346            }
347            if (type instanceof LazyType && debugMode) {
348                return type.toString();
349            }
350            if (type.isError()) {
351                return renderDefaultType(type);
352            }
353            if (KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(type) && prettyFunctionTypes) {
354                return renderFunctionType(type);
355            }
356            return renderDefaultType(type);
357        }
358    
359        @NotNull
360        private String renderFlexibleType(@NotNull JetType type) {
361            JetType lower = TypesPackage.flexibility(type).getLowerBound();
362            JetType upper = TypesPackage.flexibility(type).getUpperBound();
363    
364            String lowerRendered = renderInflexibleType(lower);
365            String upperRendered = renderInflexibleType(upper);
366    
367            if (differsOnlyInNullability(lowerRendered, upperRendered)) {
368                if (upperRendered.startsWith("(")) {
369                    // the case of complex type, e.g. (() -> Unit)?
370                    return "(" + lowerRendered + ")!";
371                }
372                return lowerRendered + "!";
373            }
374    
375            String kotlinPrefix = !shortNames ? "kotlin." : "";
376            String mutablePrefix = "Mutable";
377            // java.util.List<Foo> -> (Mutable)List<Foo!>!
378            String simpleCollection = replacePrefixes(
379                    lowerRendered, kotlinPrefix + mutablePrefix, upperRendered, kotlinPrefix, kotlinPrefix + "(" + mutablePrefix + ")"
380            );
381            if (simpleCollection != null) return simpleCollection;
382            // java.util.Map.Entry<Foo, Bar> -> (Mutable)Map.(Mutable)Entry<Foo!, Bar!>!
383            String mutableEntry = replacePrefixes(
384                    lowerRendered, kotlinPrefix + "MutableMap.MutableEntry", upperRendered, kotlinPrefix + "Map.Entry",
385                    kotlinPrefix + "(Mutable)Map.(Mutable)Entry"
386            );
387            if (mutableEntry != null) return mutableEntry;
388    
389            // Foo[] -> Array<(out) Foo!>!
390            String array = replacePrefixes(
391                    lowerRendered, kotlinPrefix + escape("Array<"), upperRendered, kotlinPrefix + escape("Array<out "),
392                    kotlinPrefix + escape("Array<(out) ")
393            );
394            if (array != null) return array;
395            return renderFlexibleTypeWithBothBounds(lower, upper);
396        }
397    
398        @Nullable
399        private static String replacePrefixes(
400                @NotNull String lowerRendered,
401                @NotNull String lowerPrefix,
402                @NotNull String upperRendered,
403                @NotNull String upperPrefix,
404                @NotNull String foldedPrefix
405        ) {
406            if (lowerRendered.startsWith(lowerPrefix) && upperRendered.startsWith(upperPrefix)) {
407                String lowerWithoutPrefix = lowerRendered.substring(lowerPrefix.length());
408                if (differsOnlyInNullability(lowerWithoutPrefix, upperRendered.substring(upperPrefix.length()))) {
409                    return foldedPrefix + lowerWithoutPrefix + "!";
410                }
411            }
412            return null;
413        }
414    
415        private static boolean differsOnlyInNullability(String lower, String upper) {
416            return lower.equals(upper.replace("?", ""))
417                   || upper.endsWith("?") && ((lower + "?").equals(upper)) || (("(" + lower + ")?").equals(upper));
418        }
419    
420        @NotNull
421        @Override
422        public String renderTypeArguments(@NotNull List<TypeProjection> typeArguments) {
423            if (typeArguments.isEmpty()) return "";
424            StringBuilder sb = new StringBuilder();
425            sb.append(lt());
426            appendTypeProjections(typeArguments, sb);
427            sb.append(gt());
428            return sb.toString();
429        }
430    
431        @NotNull
432        private String renderDefaultType(@NotNull JetType type) {
433            StringBuilder sb = new StringBuilder();
434    
435            if (type.isError()) {
436                sb.append(type.getConstructor().toString()); // Debug name of an error type is more informative
437            }
438            else {
439                sb.append(renderTypeName(type.getConstructor()));
440            }
441            sb.append(renderTypeArguments(type.getArguments()));
442            if (type.isNullable()) {
443                sb.append("?");
444            }
445            return sb.toString();
446        }
447    
448        @NotNull
449        private String renderTypeName(@NotNull TypeConstructor typeConstructor) {
450            ClassifierDescriptor cd = typeConstructor.getDeclarationDescriptor();
451            if (cd instanceof TypeParameterDescriptor) {
452                return renderName(cd.getName());
453            }
454            else if (cd instanceof ClassDescriptor) {
455                return renderClassName((ClassDescriptor) cd);
456            }
457            else {
458                assert cd == null: "Unexpected classifier: " + cd.getClass();
459                return typeConstructor.toString();
460            }
461        }
462    
463        private void appendTypeProjections(@NotNull List<TypeProjection> typeProjections, @NotNull StringBuilder builder) {
464            for (Iterator<TypeProjection> iterator = typeProjections.iterator(); iterator.hasNext(); ) {
465                TypeProjection typeProjection = iterator.next();
466                if (typeProjection.getProjectionKind() != Variance.INVARIANT) {
467                    builder.append(typeProjection.getProjectionKind()).append(" ");
468                }
469                builder.append(renderNormalizedType(typeProjection.getType()));
470                if (iterator.hasNext()) {
471                    builder.append(", ");
472                }
473            }
474        }
475    
476        @NotNull
477        private String renderFunctionType(@NotNull JetType type) {
478            StringBuilder sb = new StringBuilder();
479    
480            JetType receiverType = KotlinBuiltIns.getInstance().getReceiverType(type);
481            if (receiverType != null) {
482                sb.append(renderNormalizedType(receiverType));
483                sb.append(".");
484            }
485    
486            sb.append("(");
487            appendTypeProjections(KotlinBuiltIns.getInstance().getParameterTypeProjectionsFromFunctionType(type), sb);
488            sb.append(") ").append(arrow()).append(" ");
489            sb.append(renderNormalizedType(KotlinBuiltIns.getInstance().getReturnTypeFromFunctionType(type)));
490    
491            if (type.isNullable()) {
492                return "(" + sb + ")?";
493            }
494            return sb.toString();
495        }
496    
497    
498        /* METHODS FOR ALL KINDS OF DESCRIPTORS */
499        private void appendDefinedIn(@NotNull DeclarationDescriptor descriptor, @NotNull StringBuilder builder) {
500            if (descriptor instanceof PackageFragmentDescriptor || descriptor instanceof PackageViewDescriptor) {
501                return;
502            }
503            if (descriptor instanceof ModuleDescriptor) {
504                builder.append(" is a module");
505                return;
506            }
507            builder.append(" ").append(renderMessage("defined in")).append(" ");
508    
509            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
510            if (containingDeclaration != null) {
511                FqNameUnsafe fqName = DescriptorUtils.getFqName(containingDeclaration);
512                builder.append(FqName.ROOT.equalsTo(fqName) ? "root package" : renderFqName(fqName));
513            }
514        }
515    
516        private void renderAnnotations(@NotNull Annotated annotated, @NotNull StringBuilder builder) {
517            if (!modifiers.contains(Modifier.ANNOTATIONS)) return;
518            for (AnnotationDescriptor annotation : annotated.getAnnotations()) {
519                ClassDescriptor annotationClass = (ClassDescriptor) annotation.getType().getConstructor().getDeclarationDescriptor();
520                assert annotationClass != null;
521    
522                if (!excludedAnnotationClasses.contains(DescriptorUtils.getFqNameSafe(annotationClass))) {
523                    builder.append(renderAnnotation(annotation)).append(" ");
524                }
525            }
526        }
527    
528        @Override
529        @NotNull
530        public String renderAnnotation(@NotNull AnnotationDescriptor annotation) {
531            StringBuilder sb = new StringBuilder();
532            sb.append(renderType(annotation.getType()));
533            if (verbose) {
534                sb.append("(").append(UtilsPackage.join(renderAndSortAnnotationArguments(annotation), ", ")).append(")");
535            }
536            return sb.toString();
537        }
538    
539        @NotNull
540        private List<String> renderAndSortAnnotationArguments(@NotNull AnnotationDescriptor descriptor) {
541            Set<Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>>> valueArguments = descriptor.getAllValueArguments().entrySet();
542            List<String> resultList = new ArrayList<String>(valueArguments.size());
543            for (Map.Entry<ValueParameterDescriptor, CompileTimeConstant<?>> entry : valueArguments) {
544                CompileTimeConstant<?> value = entry.getValue();
545                String typeSuffix = ": " + renderType(value.getType(KotlinBuiltIns.getInstance()));
546                resultList.add(entry.getKey().getName().asString() + " = " + renderConstant(value) + typeSuffix);
547            }
548            Collections.sort(resultList);
549            return resultList;
550        }
551    
552        @NotNull
553        private String renderConstant(@NotNull CompileTimeConstant<?> value) {
554            return value.accept(
555                    new DefaultAnnotationArgumentVisitor<String, Void>() {
556                        @Override
557                        public String visitValue(@NotNull CompileTimeConstant<?> value, Void data) {
558                            return value.toString();
559                        }
560    
561                        @Override
562                        public String visitArrayValue(ArrayValue value, Void data) {
563                            List<String> renderedElements =
564                                    KotlinPackage.map(value.getValue(),
565                                                      new Function1<CompileTimeConstant<?>, String>() {
566                                                          @Override
567                                                          public String invoke(CompileTimeConstant<?> constant) {
568                                                              return renderConstant(constant);
569                                                          }
570                                                      });
571                            return "{" + UtilsPackage.join(renderedElements, ", ") + "}";
572                        }
573    
574                        @Override
575                        public String visitAnnotationValue(AnnotationValue value, Void data) {
576                            return renderAnnotation(value.getValue());
577                        }
578    
579                        @Override
580                        public String visitJavaClassValue(JavaClassValue value, Void data) {
581                            return renderType(value.getValue()) + ".class";
582                        }
583                    },
584                    null
585            );
586        }
587    
588        private void renderVisibility(@NotNull Visibility visibility, @NotNull StringBuilder builder) {
589            if (!modifiers.contains(Modifier.VISIBILITY)) return;
590            if (normalizedVisibilities) {
591                visibility = visibility.normalize();
592            }
593            if (!showInternalKeyword && visibility == Visibilities.INTERNAL) return;
594            builder.append(renderKeyword(visibility.toString())).append(" ");
595        }
596    
597        private void renderModality(@NotNull Modality modality, @NotNull StringBuilder builder) {
598            if (!modifiers.contains(Modifier.MODALITY)) return;
599            String keyword = modality.name().toLowerCase();
600            builder.append(renderKeyword(keyword)).append(" ");
601        }
602    
603        private void renderInner(boolean isInner, @NotNull StringBuilder builder) {
604            if (!modifiers.contains(Modifier.INNER)) return;
605            if (isInner) {
606                builder.append(renderKeyword("inner")).append(" ");
607            }
608        }
609    
610        private void renderModalityForCallable(@NotNull CallableMemberDescriptor callable, @NotNull StringBuilder builder) {
611            if (!DescriptorUtils.isTopLevelDeclaration(callable) || callable.getModality() != Modality.FINAL) {
612                if (overridesSomething(callable)
613                    && overrideRenderingPolicy == OverrideRenderingPolicy.RENDER_OVERRIDE
614                    && callable.getModality() == Modality.OPEN) {
615                    return;
616                }
617                renderModality(callable.getModality(), builder);
618            }
619        }
620    
621        private boolean overridesSomething(CallableMemberDescriptor callable) {
622            return !callable.getOverriddenDescriptors().isEmpty();
623        }
624    
625        private void renderOverride(@NotNull CallableMemberDescriptor callableMember, @NotNull StringBuilder builder) {
626            if (!modifiers.contains(Modifier.OVERRIDE)) return;
627            if (overridesSomething(callableMember)) {
628                if (overrideRenderingPolicy != OverrideRenderingPolicy.RENDER_OPEN) {
629                    builder.append("override ");
630                    if (verbose) {
631                        builder.append("/*").append(callableMember.getOverriddenDescriptors().size()).append("*/ ");
632                    }
633                }
634            }
635        }
636    
637        private void renderMemberKind(CallableMemberDescriptor callableMember, StringBuilder builder) {
638            if (!modifiers.contains(Modifier.MEMBER_KIND)) return;
639            if (verbose && callableMember.getKind() != CallableMemberDescriptor.Kind.DECLARATION) {
640                builder.append("/*").append(callableMember.getKind().name().toLowerCase()).append("*/ ");
641            }
642        }
643    
644        @NotNull
645        @Override
646        public String render(@NotNull DeclarationDescriptor declarationDescriptor) {
647            StringBuilder stringBuilder = new StringBuilder();
648            declarationDescriptor.accept(new RenderDeclarationDescriptorVisitor(), stringBuilder);
649    
650            if (withDefinedIn) {
651                appendDefinedIn(declarationDescriptor, stringBuilder);
652            }
653            return stringBuilder.toString();
654        }
655    
656    
657        /* TYPE PARAMETERS */
658        private void renderTypeParameter(@NotNull TypeParameterDescriptor typeParameter, @NotNull StringBuilder builder, boolean topLevel) {
659            if (topLevel) {
660                builder.append(lt());
661            }
662    
663            if (verbose) {
664                builder.append("/*").append(typeParameter.getIndex()).append("*/ ");
665            }
666    
667            if (typeParameter.isReified()) {
668                builder.append(renderKeyword("reified")).append(" ");
669            }
670            String variance = typeParameter.getVariance().toString();
671            if (!variance.isEmpty()) {
672                builder.append(renderKeyword(variance)).append(" ");
673            }
674            renderName(typeParameter, builder);
675            int upperBoundsCount = typeParameter.getUpperBounds().size();
676            if ((upperBoundsCount > 1 && !topLevel) || upperBoundsCount == 1) {
677                JetType upperBound = typeParameter.getUpperBounds().iterator().next();
678                if (!KotlinBuiltIns.getInstance().getDefaultBound().equals(upperBound)) {
679                    builder.append(" : ").append(renderType(upperBound));
680                }
681            }
682            else if (topLevel) {
683                boolean first = true;
684                for (JetType upperBound : typeParameter.getUpperBounds()) {
685                    if (upperBound.equals(KotlinBuiltIns.getInstance().getDefaultBound())) {
686                        continue;
687                    }
688                    if (first) {
689                        builder.append(" : ");
690                    }
691                    else {
692                        builder.append(" & ");
693                    }
694                    builder.append(renderType(upperBound));
695                    first = false;
696                }
697            }
698            else {
699                // rendered with "where"
700            }
701    
702            if (topLevel) {
703                builder.append(gt());
704            }
705        }
706    
707        private void renderTypeParameters(
708                @NotNull List<TypeParameterDescriptor> typeParameters,
709                @NotNull StringBuilder builder,
710                boolean withSpace
711        ) {
712            if (withoutTypeParameters) return;
713    
714            if (!typeParameters.isEmpty()) {
715                builder.append(lt());
716                for (Iterator<TypeParameterDescriptor> iterator = typeParameters.iterator(); iterator.hasNext(); ) {
717                    TypeParameterDescriptor typeParameterDescriptor = iterator.next();
718                    renderTypeParameter(typeParameterDescriptor, builder, false);
719                    if (iterator.hasNext()) {
720                        builder.append(", ");
721                    }
722                }
723                builder.append(gt());
724                if (withSpace) {
725                    builder.append(" ");
726                }
727            }
728        }
729    
730        /* FUNCTIONS */
731        private void renderFunction(@NotNull FunctionDescriptor function, @NotNull StringBuilder builder) {
732            if (!startFromName) {
733                renderAnnotations(function, builder);
734                renderVisibility(function.getVisibility(), builder);
735                renderModalityForCallable(function, builder);
736                renderOverride(function, builder);
737                renderMemberKind(function, builder);
738    
739                builder.append(renderKeyword("fun")).append(" ");
740                renderTypeParameters(function.getTypeParameters(), builder, true);
741                renderReceiver(function, builder);
742            }
743    
744            renderName(function, builder);
745    
746            renderValueParameters(function, builder);
747    
748            renderReceiverAfterName(function, builder);
749    
750            JetType returnType = function.getReturnType();
751            if (unitReturnType || (returnType == null || !KotlinBuiltIns.getInstance().isUnit(returnType))) {
752                builder.append(": ").append(returnType == null ? "[NULL]" : escape(renderType(returnType)));
753            }
754    
755            renderWhereSuffix(function.getTypeParameters(), builder);
756        }
757    
758        private void renderReceiverAfterName(CallableDescriptor callableDescriptor, StringBuilder builder) {
759            if (!receiverAfterName) return;
760    
761            ReceiverParameterDescriptor receiver = callableDescriptor.getExtensionReceiverParameter();
762            if (receiver != null) {
763                builder.append(" on ").append(escape(renderType(receiver.getType())));
764            }
765        }
766    
767        private void renderReceiver(CallableDescriptor callableDescriptor, StringBuilder builder) {
768            ReceiverParameterDescriptor receiver = callableDescriptor.getExtensionReceiverParameter();
769            if (receiver != null) {
770                builder.append(escape(renderType(receiver.getType()))).append(".");
771            }
772        }
773    
774        private void renderConstructor(@NotNull ConstructorDescriptor constructor, @NotNull StringBuilder builder) {
775            renderAnnotations(constructor, builder);
776            renderVisibility(constructor.getVisibility(), builder);
777            renderMemberKind(constructor, builder);
778    
779            builder.append(renderKeyword("constructor")).append(" ");
780    
781            ClassDescriptor classDescriptor = constructor.getContainingDeclaration();
782            renderName(classDescriptor, builder);
783    
784            renderTypeParameters(classDescriptor.getTypeConstructor().getParameters(), builder, false);
785            renderValueParameters(constructor, builder);
786            renderWhereSuffix(constructor.getTypeParameters(), builder);
787        }
788    
789        private void renderWhereSuffix(@NotNull List<TypeParameterDescriptor> typeParameters, @NotNull StringBuilder builder) {
790            if (withoutTypeParameters) return;
791    
792            List<String> upperBoundStrings = new ArrayList<String>(0);
793    
794            for (TypeParameterDescriptor typeParameter : typeParameters) {
795                if (typeParameter.getUpperBounds().size() > 1) {
796                    boolean first = true;
797                    for (JetType upperBound : typeParameter.getUpperBounds()) {
798                        // first parameter is rendered by renderTypeParameter:
799                        if (!first) {
800                            upperBoundStrings.add(renderName(typeParameter.getName()) + " : " + escape(renderType(upperBound)));
801                        }
802                        first = false;
803                    }
804                }
805            }
806            if (!upperBoundStrings.isEmpty()) {
807                builder.append(" ").append(renderKeyword("where")).append(" ");
808                builder.append(UtilsPackage.join(upperBoundStrings, ", "));
809            }
810        }
811    
812        @NotNull
813        @Override
814        public String renderFunctionParameters(@NotNull FunctionDescriptor functionDescriptor) {
815            StringBuilder stringBuilder = new StringBuilder();
816            renderValueParameters(functionDescriptor, stringBuilder);
817            return stringBuilder.toString();
818        }
819    
820        private void renderValueParameters(@NotNull FunctionDescriptor function, @NotNull StringBuilder builder) {
821            boolean includeNames = !withoutFunctionParameterNames &&
822                                   (includeSynthesizedParameterNames || !function.hasSynthesizedParameterNames());
823            handler.appendBeforeValueParameters(function, builder);
824            for (ValueParameterDescriptor parameter : function.getValueParameters()) {
825                handler.appendBeforeValueParameter(parameter, builder);
826                renderValueParameter(parameter, includeNames, builder, false);
827                handler.appendAfterValueParameter(parameter, builder);
828            }
829            handler.appendAfterValueParameters(function, builder);
830        }
831    
832        /* VARIABLES */
833        private void renderValueParameter(@NotNull ValueParameterDescriptor valueParameter, boolean includeName, @NotNull StringBuilder builder, boolean topLevel) {
834            if (topLevel) {
835                builder.append(renderKeyword("value-parameter")).append(" ");
836            }
837    
838            if (verbose) {
839                builder.append("/*").append(valueParameter.getIndex()).append("*/ ");
840            }
841    
842            renderAnnotations(valueParameter, builder);
843            renderVariable(valueParameter, includeName, builder, topLevel);
844            boolean withDefaultValue = renderDefaultValues && (debugMode ? valueParameter.declaresDefaultValue() : valueParameter.hasDefaultValue());
845            if (withDefaultValue) {
846                builder.append(" = ...");
847            }
848        }
849    
850        private void renderValVarPrefix(@NotNull VariableDescriptor variable, @NotNull StringBuilder builder) {
851            builder.append(renderKeyword(variable.isVar() ? "var" : "val")).append(" ");
852        }
853    
854        private void renderVariable(@NotNull VariableDescriptor variable, boolean includeName, @NotNull StringBuilder builder, boolean topLevel) {
855            JetType realType = variable.getType();
856    
857            JetType varargElementType = variable instanceof ValueParameterDescriptor
858                                        ? ((ValueParameterDescriptor) variable).getVarargElementType()
859                                        : null;
860            JetType typeToRender = varargElementType != null ? varargElementType : realType;
861    
862            if (varargElementType != null) {
863                builder.append(renderKeyword("vararg")).append(" ");
864            }
865            if (topLevel && !startFromName) {
866                renderValVarPrefix(variable, builder);
867            }
868    
869            if (includeName) {
870                renderName(variable, builder);
871                builder.append(": ");
872            }
873    
874            builder.append(escape(renderType(typeToRender)));
875    
876            renderInitializer(variable, builder);
877    
878            if (verbose && varargElementType != null) {
879                builder.append(" /*").append(escape(renderType(realType))).append("*/");
880            }
881        }
882    
883        private void renderProperty(@NotNull PropertyDescriptor property, @NotNull StringBuilder builder) {
884            if (!startFromName) {
885                renderAnnotations(property, builder);
886                renderVisibility(property.getVisibility(), builder);
887                renderModalityForCallable(property, builder);
888                renderOverride(property, builder);
889                renderMemberKind(property, builder);
890                renderValVarPrefix(property, builder);
891                renderTypeParameters(property.getTypeParameters(), builder, true);
892                renderReceiver(property, builder);
893            }
894    
895            renderName(property, builder);
896            builder.append(": ").append(escape(renderType(property.getType())));
897    
898            renderReceiverAfterName(property, builder);
899    
900            renderInitializer(property, builder);
901    
902            renderWhereSuffix(property.getTypeParameters(), builder);
903        }
904    
905        private void renderInitializer(@NotNull VariableDescriptor variable, @NotNull StringBuilder builder) {
906            if (includePropertyConstant) {
907                CompileTimeConstant<?> initializer = variable.getCompileTimeInitializer();
908                if (initializer != null) {
909                    builder.append(" = ").append(escape(renderConstant(initializer)));
910                }
911            }
912        }
913    
914        /* CLASSES */
915        private void renderClass(@NotNull ClassDescriptor klass, @NotNull StringBuilder builder) {
916            if (!startFromName) {
917                renderAnnotations(klass, builder);
918                renderVisibility(klass.getVisibility(), builder);
919                if (!(klass.getKind() == ClassKind.TRAIT && klass.getModality() == Modality.ABSTRACT
920                    || klass.getKind().isSingleton() && klass.getModality() == Modality.FINAL)) {
921                    renderModality(klass.getModality(), builder);
922                }
923                renderInner(klass.isInner(), builder);
924                renderClassKindPrefix(klass, builder);
925            }
926    
927            if (klass.getKind() != ClassKind.CLASS_OBJECT) {
928                if (!startFromName) renderSpaceIfNeeded(builder);
929                renderName(klass, builder);
930            }
931            else {
932                renderClassObjectName(klass, builder);
933            }
934    
935            List<TypeParameterDescriptor> typeParameters = klass.getTypeConstructor().getParameters();
936            renderTypeParameters(typeParameters, builder, false);
937    
938            if (!klass.getKind().isSingleton() && classWithPrimaryConstructor) {
939                ConstructorDescriptor primaryConstructor = klass.getUnsubstitutedPrimaryConstructor();
940                if (primaryConstructor != null) {
941                    renderValueParameters(primaryConstructor, builder);
942                }
943            }
944    
945            renderSuperTypes(klass, builder);
946            renderWhereSuffix(typeParameters, builder);
947        }
948    
949        private void renderSuperTypes(@NotNull ClassDescriptor klass, @NotNull StringBuilder builder) {
950            if (withoutSuperTypes) return;
951    
952            if (!klass.equals(KotlinBuiltIns.getInstance().getNothing())) {
953                Collection<JetType> supertypes = klass.getTypeConstructor().getSupertypes();
954    
955                if (supertypes.isEmpty() ||
956                    supertypes.size() == 1 && KotlinBuiltIns.getInstance().isAnyOrNullableAny(supertypes.iterator().next())) {
957                }
958                else {
959                    renderSpaceIfNeeded(builder);
960                    builder.append(": ");
961                    for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
962                        JetType supertype = iterator.next();
963                        builder.append(renderType(supertype));
964                        if (iterator.hasNext()) {
965                            builder.append(", ");
966                        }
967                    }
968                }
969            }
970        }
971    
972        private void renderClassKindPrefix(ClassDescriptor klass, StringBuilder builder) {
973            builder.append(renderKeyword(getClassKindPrefix(klass)));
974        }
975    
976        @NotNull
977        public static String getClassKindPrefix(@NotNull ClassDescriptor klass) {
978            switch (klass.getKind()) {
979                case CLASS:
980                    return "class";
981                case TRAIT:
982                    return "trait";
983                case ENUM_CLASS:
984                    return "enum class";
985                case OBJECT:
986                    return "object";
987                case ANNOTATION_CLASS:
988                    return "annotation class";
989                case CLASS_OBJECT:
990                    return "class object";
991                case ENUM_ENTRY:
992                    return "enum entry";
993                default:
994                    throw new IllegalStateException("unknown class kind: " + klass.getKind());
995            }
996        }
997    
998    
999        /* OTHER */
1000        private void renderModuleOrScript(@NotNull DeclarationDescriptor moduleOrScript, @NotNull StringBuilder builder) {
1001            renderName(moduleOrScript, builder);
1002        }
1003    
1004        private void renderPackageView(@NotNull PackageViewDescriptor packageView, @NotNull StringBuilder builder) {
1005            builder.append(renderKeyword("package")).append(" ");
1006            builder.append(renderFqName(packageView.getFqName()));
1007            if (debugMode) {
1008                builder.append(" in context of ");
1009                renderName(packageView.getModule(), builder);
1010            }
1011        }
1012    
1013        private void renderPackageFragment(@NotNull PackageFragmentDescriptor fragment, @NotNull StringBuilder builder) {
1014            builder.append(renderKeyword("package-fragment")).append(" ");
1015            builder.append(renderFqName(fragment.getFqName()));
1016            if (debugMode) {
1017                builder.append(" in ");
1018                renderName(fragment.getContainingDeclaration(), builder);
1019            }
1020        }
1021    
1022    
1023        /* STUPID DISPATCH-ONLY VISITOR */
1024        private class RenderDeclarationDescriptorVisitor extends DeclarationDescriptorVisitorEmptyBodies<Void, StringBuilder> {
1025            @Override
1026            public Void visitValueParameterDescriptor(ValueParameterDescriptor descriptor, StringBuilder builder) {
1027                renderValueParameter(descriptor, true, builder, true);
1028                return null;
1029            }
1030    
1031            @Override
1032            public Void visitVariableDescriptor(VariableDescriptor descriptor, StringBuilder builder) {
1033                renderVariable(descriptor, true, builder, true);
1034                return null;
1035            }
1036    
1037            @Override
1038            public Void visitPropertyDescriptor(PropertyDescriptor descriptor, StringBuilder builder) {
1039                renderProperty(descriptor, builder);
1040                return null;
1041            }
1042    
1043            @Override
1044            public Void visitFunctionDescriptor(FunctionDescriptor descriptor, StringBuilder builder) {
1045                renderFunction(descriptor, builder);
1046                return null;
1047            }
1048    
1049            @Override
1050            public Void visitReceiverParameterDescriptor(ReceiverParameterDescriptor descriptor, StringBuilder data) {
1051                throw new UnsupportedOperationException("Don't render receiver parameters");
1052            }
1053    
1054            @Override
1055            public Void visitConstructorDescriptor(ConstructorDescriptor constructorDescriptor, StringBuilder builder) {
1056                renderConstructor(constructorDescriptor, builder);
1057                return null;
1058            }
1059    
1060            @Override
1061            public Void visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, StringBuilder builder) {
1062                renderTypeParameter(descriptor, builder, true);
1063                return null;
1064            }
1065    
1066            @Override
1067            public Void visitPackageFragmentDescriptor(
1068                    PackageFragmentDescriptor descriptor, StringBuilder builder
1069            ) {
1070                renderPackageFragment(descriptor, builder);
1071                return null;
1072            }
1073    
1074            @Override
1075            public Void visitPackageViewDescriptor(
1076                    PackageViewDescriptor descriptor, StringBuilder builder
1077            ) {
1078                renderPackageView(descriptor, builder);
1079                return null;
1080            }
1081    
1082            @Override
1083            public Void visitModuleDeclaration(ModuleDescriptor descriptor, StringBuilder builder) {
1084                renderModuleOrScript(descriptor, builder);
1085                return null;
1086            }
1087    
1088            @Override
1089            public Void visitScriptDescriptor(ScriptDescriptor scriptDescriptor, StringBuilder builder) {
1090                renderModuleOrScript(scriptDescriptor, builder);
1091                return null;
1092            }
1093    
1094            @Override
1095            public Void visitClassDescriptor(ClassDescriptor descriptor, StringBuilder builder) {
1096                renderClass(descriptor, builder);
1097                return null;
1098            }
1099       }
1100    }