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    
017    package org.jetbrains.jet.lang.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.jet.lang.descriptors.CallableDescriptor;
025    import org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.*;
026    import org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.TextRenderer.TextElement;
027    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
028    import org.jetbrains.jet.lang.types.JetType;
029    import org.jetbrains.jet.renderer.DescriptorRenderer;
030    
031    import java.util.Iterator;
032    import java.util.List;
033    
034    public 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    }