b5f18c246efcbd405282b0f4f4a3dddddf2e9d59
[idea/community.git] / platform / analysis-impl / src / com / intellij / codeInspection / ex / HTMLComposerImpl.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * Created by IntelliJ IDEA.
19  * User: max
20  * Date: Dec 22, 2001
21  * Time: 4:54:17 PM
22  * To change template for new interface use
23  * Code Style | Class Templates options (Tools | IDE Options).
24  */
25 package com.intellij.codeInspection.ex;
26
27 import com.intellij.codeInspection.CommonProblemDescriptor;
28 import com.intellij.codeInspection.HTMLComposer;
29 import com.intellij.codeInspection.InspectionsBundle;
30 import com.intellij.codeInspection.export.HTMLExporter;
31 import com.intellij.codeInspection.lang.HTMLComposerExtension;
32 import com.intellij.codeInspection.lang.InspectionExtensionsFactory;
33 import com.intellij.codeInspection.reference.*;
34 import com.intellij.lang.Language;
35 import com.intellij.openapi.extensions.Extensions;
36 import com.intellij.openapi.project.ProjectUtilCore;
37 import com.intellij.openapi.util.Key;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.psi.PsiElement;
40 import com.intellij.psi.PsiFile;
41 import com.intellij.psi.util.PsiUtilCore;
42 import org.jetbrains.annotations.NonNls;
43 import org.jetbrains.annotations.NotNull;
44 import org.jetbrains.annotations.Nullable;
45
46 import java.util.HashMap;
47 import java.util.Map;
48
49 /**
50  * @author max
51  */
52 public abstract class HTMLComposerImpl extends HTMLComposer {
53   protected HTMLExporter myExporter;
54   private final int[] myListStack;
55   private int myListStackTop;
56   private final Map<Key, HTMLComposerExtension> myExtensions = new HashMap<Key, HTMLComposerExtension>();
57   private final Map<Language, HTMLComposerExtension> myLanguageExtensions = new HashMap<Language, HTMLComposerExtension>();
58   @NonNls protected static final String BR = "<br>";
59   @NonNls protected static final String NBSP = "&nbsp;";
60   @NonNls protected static final String CODE_CLOSING = "</code>";
61   @NonNls protected static final String CODE_OPENING = "<code>";
62   @NonNls protected static final String B_OPENING = "<b>";
63   @NonNls protected static final String B_CLOSING = "</b>";
64
65   @NonNls protected static final String CLOSE_TAG = "\">";
66   @NonNls protected static final String A_HREF_OPENING = "<a HREF=\"";
67   @NonNls protected static final String A_CLOSING = "</a>";
68
69   protected HTMLComposerImpl() {
70     myListStack = new int[5];
71     myListStackTop = -1;
72     for (InspectionExtensionsFactory factory : Extensions.getExtensions(InspectionExtensionsFactory.EP_NAME)) {
73       final HTMLComposerExtension extension = factory.createHTMLComposerExtension(this);
74       if (extension != null) {
75         myExtensions.put(extension.getID(), extension);
76         myLanguageExtensions.put(extension.getLanguage(), extension);
77       }
78     }
79   }
80
81   public abstract void compose(StringBuffer buf, RefEntity refEntity);
82
83   public void compose(StringBuffer buf, RefEntity refElement, CommonProblemDescriptor descriptor) {}
84
85   public void composeWithExporter(StringBuffer buf, RefEntity refEntity, HTMLExporter exporter) {
86     myExporter = exporter;
87     compose(buf, refEntity);
88     myExporter = null;
89   }
90
91   protected void genPageHeader(final StringBuffer buf, RefEntity refEntity) {
92     if (refEntity instanceof RefElement) {
93       RefElement refElement = (RefElement)refEntity;
94
95       appendHeading(buf, InspectionsBundle.message("inspection.offline.view.tool.display.name.title"));
96       buf.append(BR);
97       appendAfterHeaderIndention(buf);
98
99       appendShortName(buf, refElement);
100       buf.append(BR).append(BR);
101
102       appendHeading(buf, InspectionsBundle.message("inspection.export.results.capitalized.location"));
103       buf.append(BR);
104       appendAfterHeaderIndention(buf);
105       appendLocation(buf, refElement);
106       buf.append(BR).append(BR);
107     }
108   }
109
110   private void appendLocation(final StringBuffer buf, final RefElement refElement) {
111     final HTMLComposerExtension extension = getLanguageExtension(refElement);
112     if (extension != null) {
113       extension.appendLocation(refElement, buf);
114     }
115     if (refElement instanceof RefFile){
116       buf.append(InspectionsBundle.message("inspection.export.results.file"));
117       buf.append(NBSP);
118       appendElementReference(buf, refElement, false);
119     }
120   }
121
122   @Nullable
123   private HTMLComposerExtension getLanguageExtension(final RefElement refElement) {
124     final PsiElement element = refElement.getElement();
125     return element != null ? myLanguageExtensions.get(element.getLanguage()) : null;
126   }
127
128   private void appendShortName(final StringBuffer buf, RefElement refElement) {
129     final HTMLComposerExtension extension = getLanguageExtension(refElement);
130     if (extension != null) {
131       extension.appendShortName(refElement, buf);
132     } else {
133       refElement.accept(new RefVisitor() {
134         @Override public void visitFile(@NotNull RefFile file) {
135           final PsiFile psiFile = file.getElement();
136           if (psiFile != null) {
137             buf.append(B_OPENING);
138             buf.append(psiFile.getName());
139             buf.append(B_CLOSING);
140           }
141         }
142       });
143     }
144   }
145
146   protected void appendQualifiedName(StringBuffer buf, RefEntity refEntity) {
147     if (refEntity == null) return;
148     String qName = "";
149
150     while (!(refEntity instanceof RefProject)) {
151       if (qName.length() > 0) qName = "." + qName;
152
153       String name = null;
154       if (refEntity instanceof RefElement) {
155         final HTMLComposerExtension extension = getLanguageExtension((RefElement)refEntity);
156         if (extension != null) {
157           name = extension.getQualifiedName(refEntity);
158         }
159       }
160
161       if (name == null) {
162         name = refEntity.getName();
163       }
164
165       qName = name + qName;
166       refEntity = refEntity.getOwner();
167     }
168
169     buf.append(qName);
170   }
171
172   @Override
173   public void appendElementReference(final StringBuffer buf, RefElement refElement) {
174     appendElementReference(buf, refElement, true);
175   }
176
177   @Override
178   public void appendElementReference(final StringBuffer buf, RefElement refElement, String linkText, @NonNls String frameName) {
179     if (myExporter == null) {
180       final String url = ((RefElementImpl)refElement).getURL();
181       if (url != null) {
182         appendElementReference(buf, url, linkText, frameName);
183       }
184     }
185     else {
186       appendElementReference(buf, myExporter.getURL(refElement), linkText, frameName);
187     }
188   }
189
190   @Override
191   public void appendElementReference(final StringBuffer buf, String url, String linkText, @NonNls String frameName) {
192     buf.append(A_HREF_OPENING);
193     buf.append(url);
194     if (frameName != null) {
195       @NonNls final String target = "\" target=\"";
196       buf.append(target);
197       buf.append(frameName);
198     }
199
200     buf.append("\">");
201     buf.append(linkText);
202     buf.append(A_CLOSING);
203   }
204
205   protected void appendQuickFix(@NonNls final StringBuffer buf, String text, int index) {
206     if (myExporter == null) {
207       buf.append("<a HREF=\"file://bred.txt#invoke:").append(index);
208       buf.append("\">");
209       buf.append(text);
210       buf.append("</a>");
211     }
212   }
213
214   @Override
215   public void appendElementReference(final StringBuffer buf, RefElement refElement, boolean isPackageIncluded) {
216     final HTMLComposerExtension extension = getLanguageExtension(refElement);
217
218     if (extension != null) {
219       extension.appendReferencePresentation(refElement, buf, isPackageIncluded);
220     } else if (refElement instanceof RefFile) {
221       buf.append(A_HREF_OPENING);
222
223       if (myExporter == null) {
224         buf.append(((RefElementImpl)refElement).getURL());
225       }
226       else {
227         buf.append(myExporter.getURL(refElement));
228       }
229
230       buf.append("\">");
231       String refElementName = refElement.getName();
232       final PsiElement element = refElement.getElement();
233       if (element != null) {
234         final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element);
235         if (virtualFile != null) {
236           refElementName = ProjectUtilCore.displayUrlRelativeToProject(virtualFile, virtualFile.getPresentableUrl(), element.getProject(),
237                                                                        true, false);
238         }
239       }
240       buf.append(refElementName);
241       buf.append(A_CLOSING);
242     }
243   }
244
245   @Override
246   public String composeNumereables(int n, String statement, String singleEnding, String multipleEnding) {
247     final StringBuilder buf = new StringBuilder();
248     buf.append(n);
249     buf.append(' ');
250     buf.append(statement);
251
252     if (n % 10 == 1 && n % 100 != 11) {
253       buf.append(singleEnding);
254     }
255     else {
256       buf.append(multipleEnding);
257     }
258     return buf.toString();
259   }
260
261   @Override
262   public void appendElementInReferences(StringBuffer buf, RefElement refElement) {
263     if (refElement.getInReferences().size() > 0) {
264       appendHeading(buf, InspectionsBundle.message("inspection.export.results.used.from"));
265       startList(buf);
266       for (RefElement refCaller : refElement.getInReferences()) {
267         appendListItem(buf, refCaller);
268       }
269       doneList(buf);
270     }
271   }
272
273   @Override
274   public void appendElementOutReferences(StringBuffer buf, RefElement refElement) {
275     if (refElement.getOutReferences().size() > 0) {
276       appendHeading(buf, InspectionsBundle.message("inspection.export.results.uses"));
277       startList(buf);
278       for (RefElement refCallee : refElement.getOutReferences()) {
279         appendListItem(buf, refCallee);
280       }
281       doneList(buf);
282     }
283   }
284
285   @Override
286   public void appendListItem(StringBuffer buf, RefElement refElement) {
287     startListItem(buf);
288     appendElementReference(buf, refElement, true);
289     appendAdditionalListItemInfo(buf, refElement);
290     doneListItem(buf);
291   }
292
293   protected void appendAdditionalListItemInfo(StringBuffer buf, RefElement refElement) {
294     // Default appends nothing.
295   }
296
297   protected void appendResolution(StringBuffer buf, RefEntity where, String[] quickFixes) {
298     if (myExporter != null) return;
299     if (where instanceof RefElement && !where.isValid()) return;
300     if (quickFixes != null) {
301       boolean listStarted = false;
302       for (int i = 0; i < quickFixes.length; i++) {
303         final String text = quickFixes[i];
304         if (text == null) continue;
305         if (!listStarted) {
306           appendHeading(buf, InspectionsBundle.message("inspection.problem.resolution"));
307           startList(buf);
308           listStarted = true;
309         }
310         startListItem(buf);
311         appendQuickFix(buf, text, i);
312         doneListItem(buf);
313       }
314
315       if (listStarted) {
316         doneList(buf);
317       }
318     }
319   }
320
321
322   @Override
323   public void startList(@NonNls final StringBuffer buf) {
324     if (myListStackTop == -1) {
325       buf.append("<div class=\"problem-description\">");
326     }
327     buf.append("<ul>");
328     myListStackTop++;
329     myListStack[myListStackTop] = 0;
330   }
331
332   @Override
333   public void doneList(@NonNls StringBuffer buf) {
334     buf.append("</ul>");
335     if (myListStack[myListStackTop] != 0) {
336       buf.append("<table cellpadding=\"0\" border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr></table>");
337     }
338     if (myListStackTop == 0) {
339       buf.append("</div>");
340     }
341     myListStackTop--;
342   }
343
344   @Override
345   public void startListItem(@NonNls StringBuffer buf) {
346     myListStack[myListStackTop]++;
347     buf.append("<li>");
348   }
349
350   public static void doneListItem(@NonNls StringBuffer buf) {
351     buf.append("</li>");
352   }
353
354   @Override
355   public void appendNoProblems(StringBuffer buf) {
356     buf.append("<p class=\"problem-description-group\">");;
357     buf.append(InspectionsBundle.message("inspection.export.results.no.problems.found"));
358     buf.append("</p>");
359   }
360
361   @Override
362   public <T> T getExtension(final Key<T> key) {
363     return (T)myExtensions.get(key);
364   }
365 }