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