001    /*
002     * Copyright 2010-2016 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.kotlin.diagnostics.rendering;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.base.Predicates;
021    import com.google.common.collect.Lists;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.descriptors.CallableDescriptor;
025    import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.DescriptorRow;
026    import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.FunctionArgumentsRow;
027    import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.TableRow;
028    import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TextRenderer.TextElement;
029    import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition;
030    import org.jetbrains.kotlin.types.KotlinType;
031    
032    import java.util.ArrayList;
033    import java.util.Iterator;
034    import java.util.List;
035    
036    public class TabledDescriptorRenderer {
037        public interface TableOrTextRenderer {}
038    
039        public static class TableRenderer implements TableOrTextRenderer{
040            public interface TableRow {
041            }
042    
043            public static class DescriptorRow implements TableRow {
044                public final CallableDescriptor descriptor;
045    
046                public DescriptorRow(CallableDescriptor descriptor) {
047                    this.descriptor = descriptor;
048                }
049            }
050    
051            public static class FunctionArgumentsRow implements TableRow {
052                public final KotlinType receiverType;
053                public final List<KotlinType> argumentTypes;
054                public final Predicate<ConstraintPosition> isErrorPosition;
055    
056                public FunctionArgumentsRow(KotlinType receiverType, List<KotlinType> argumentTypes, Predicate<ConstraintPosition> isErrorPosition) {
057                    this.receiverType = receiverType;
058                    this.argumentTypes = argumentTypes;
059                    this.isErrorPosition = isErrorPosition;
060                }
061            }
062    
063            public final List<TableRow> rows = Lists.newArrayList();
064    
065            public TableRenderer descriptor(CallableDescriptor descriptor) {
066                rows.add(new DescriptorRow(descriptor));
067                return this;
068            }
069    
070            public TableRenderer functionArgumentTypeList(@Nullable KotlinType receiverType, @NotNull List<KotlinType> argumentTypes) {
071    
072                return functionArgumentTypeList(receiverType, argumentTypes, Predicates.<ConstraintPosition>alwaysFalse());
073            }
074    
075            public TableRenderer functionArgumentTypeList(@Nullable KotlinType receiverType,
076                    @NotNull List<KotlinType> argumentTypes,
077                    @NotNull Predicate<ConstraintPosition> isErrorPosition) {
078                rows.add(new FunctionArgumentsRow(receiverType, argumentTypes, isErrorPosition));
079                return this;
080            }
081    
082            public TableRenderer text(@NotNull String text) {
083                rows.add(newText().normal(text));
084                return this;
085            }
086    
087            public TableRenderer text(@NotNull TextRenderer textRenderer) {
088                rows.add(textRenderer);
089                return this;
090            }
091        }
092    
093        public static class TextRenderer implements TableOrTextRenderer, TableRow {
094            public static class TextElement {
095    
096                public TextElementType type;
097                public String text;
098    
099                public TextElement(@NotNull TextElementType type, @NotNull String text) {
100                    this.type = type;
101                    this.text = text;
102                }
103            }
104    
105            public final List<TextElement> elements = Lists.newArrayList();
106    
107            public TextRenderer normal(@NotNull Object text) {
108                elements.add(new TextElement(TextElementType.DEFAULT, text.toString()));
109                return this;
110            }
111    
112            public TextRenderer error(@NotNull Object text) {
113                elements.add(new TextElement(TextElementType.ERROR, text.toString()));
114                return this;
115            }
116    
117            public TextRenderer strong(@NotNull Object text) {
118                elements.add(new TextElement(TextElementType.STRONG, text.toString()));
119                return this;
120            }
121        }
122    
123        protected final List<TableOrTextRenderer> renderers = Lists.newArrayList();
124    
125        public TabledDescriptorRenderer text(@NotNull TextRenderer textRenderer) {
126            renderers.add(textRenderer);
127            return this;
128        }
129    
130        public TabledDescriptorRenderer table(@NotNull TableRenderer tableRenderer) {
131            renderers.add(tableRenderer);
132            return this;
133        }
134    
135        public static TextRenderer newText() {
136            return new TextRenderer();
137        }
138    
139        public static TableRenderer newTable() {
140            return new TableRenderer();
141        }
142    
143        @Override
144        public String toString() {
145            StringBuilder result = new StringBuilder();
146            for (TableOrTextRenderer tableOrTextRenderer : renderers) {
147                if (tableOrTextRenderer instanceof TableRenderer) {
148                    renderTable((TableRenderer)tableOrTextRenderer, result);
149                }
150                else {
151                    renderText((TextRenderer)tableOrTextRenderer, result);
152                }
153            }
154            return result.toString();
155        }
156    
157        @NotNull
158        public DiagnosticParameterRenderer<KotlinType> getTypeRenderer() {
159            return Renderers.RENDER_TYPE;
160        }
161    
162        protected void renderText(TextRenderer textRenderer, StringBuilder result) {
163            for (TextElement element : textRenderer.elements) {
164                result.append(element.text);
165            }
166        }
167    
168        protected void renderTable(TableRenderer table, StringBuilder result) {
169            if (table.rows.isEmpty()) return;
170    
171            RenderingContext context = computeRenderingContext(table);
172            for (TableRow row : table.rows) {
173                if (row instanceof TextRenderer) {
174                    renderText((TextRenderer) row, result);
175                }
176                if (row instanceof DescriptorRow) {
177                    result.append(Renderers.COMPACT.render(((DescriptorRow) row).descriptor, context));
178                }
179                if (row instanceof FunctionArgumentsRow) {
180                    FunctionArgumentsRow functionArgumentsRow = (FunctionArgumentsRow) row;
181                    renderFunctionArguments(functionArgumentsRow.receiverType, functionArgumentsRow.argumentTypes, result, context);
182                }
183                result.append("\n");
184            }
185        }
186    
187        private void renderFunctionArguments(
188                @Nullable KotlinType receiverType,
189                @NotNull List<KotlinType> argumentTypes,
190                StringBuilder result,
191                @NotNull RenderingContext context
192        ) {
193            boolean hasReceiver = receiverType != null;
194            if (hasReceiver) {
195                result.append("receiver: ");
196                result.append(getTypeRenderer().render(receiverType, context));
197                result.append("  arguments: ");
198            }
199            if (argumentTypes.isEmpty()) {
200                result.append("()");
201                return;
202            }
203    
204            result.append("(");
205            for (Iterator<KotlinType> iterator = argumentTypes.iterator(); iterator.hasNext(); ) {
206                KotlinType argumentType = iterator.next();
207                String renderedArgument = getTypeRenderer().render(argumentType, context);
208    
209                result.append(renderedArgument);
210                if (iterator.hasNext()) {
211                    result.append(",");
212                }
213            }
214            result.append(")");
215        }
216    
217        public static TabledDescriptorRenderer create() {
218            return new TabledDescriptorRenderer();
219        }
220    
221        public static enum TextElementType { STRONG, ERROR, DEFAULT }
222    
223        @NotNull
224        protected static RenderingContext computeRenderingContext(@NotNull TableRenderer table) {
225            ArrayList<Object> toRender = new ArrayList<Object>();
226            for (TableRow row : table.rows) {
227                if (row instanceof DescriptorRow) {
228                    toRender.add(((DescriptorRow) row).descriptor);
229                }
230                else if (row instanceof FunctionArgumentsRow) {
231                    toRender.add(((FunctionArgumentsRow) row).receiverType);
232                    toRender.addAll(((FunctionArgumentsRow) row).argumentTypes);
233                }
234                else if (row instanceof TextRenderer) {
235    
236                }
237                else {
238                    throw new AssertionError("Unknown row of type " + row.getClass());
239                }
240            }
241            return new RenderingContext.Impl(toRender);
242        }
243    }