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