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 }