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 if (argumentType == null) {
208 result.append("<unknown>");
209 }
210 else {
211 String renderedArgument = getTypeRenderer().render(argumentType, context);
212 result.append(renderedArgument);
213 }
214
215 if (iterator.hasNext()) {
216 result.append(",");
217 }
218 }
219 result.append(")");
220 }
221
222 public static TabledDescriptorRenderer create() {
223 return new TabledDescriptorRenderer();
224 }
225
226 public static enum TextElementType { STRONG, ERROR, DEFAULT }
227
228 @NotNull
229 protected static RenderingContext computeRenderingContext(@NotNull TableRenderer table) {
230 ArrayList<Object> toRender = new ArrayList<Object>();
231 for (TableRow row : table.rows) {
232 if (row instanceof DescriptorRow) {
233 toRender.add(((DescriptorRow) row).descriptor);
234 }
235 else if (row instanceof FunctionArgumentsRow) {
236 toRender.add(((FunctionArgumentsRow) row).receiverType);
237 toRender.addAll(((FunctionArgumentsRow) row).argumentTypes);
238 }
239 else if (row instanceof TextRenderer) {
240
241 }
242 else {
243 throw new AssertionError("Unknown row of type " + row.getClass());
244 }
245 }
246 return new RenderingContext.Impl(toRender);
247 }
248 }