Merge remote-tracking branch 'origin/master' into develar/is
[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.export.results.capitalized.location"));
96       buf.append("<div class=\"location\">");
97       appendShortName(buf, refElement);
98       buf.append(BR);
99       buf.append("in ");
100       appendLocation(buf, refElement);
101       buf.append("</div>");
102       buf.append(BR).append(BR);
103     }
104   }
105
106   private void appendLocation(final StringBuffer buf, final RefElement refElement) {
107     final HTMLComposerExtension extension = getLanguageExtension(refElement);
108     if (extension != null) {
109       extension.appendLocation(refElement, buf);
110     }
111     if (refElement instanceof RefFile){
112       buf.append(InspectionsBundle.message("inspection.export.results.file"));
113       buf.append(NBSP);
114       appendElementReference(buf, refElement, false);
115     }
116   }
117
118   @Nullable
119   private HTMLComposerExtension getLanguageExtension(final RefElement refElement) {
120     final PsiElement element = refElement.getElement();
121     return element != null ? myLanguageExtensions.get(element.getLanguage()) : null;
122   }
123
124   private void appendShortName(final StringBuffer buf, RefElement refElement) {
125     final HTMLComposerExtension extension = getLanguageExtension(refElement);
126     if (extension != null) {
127       extension.appendShortName(refElement, buf);
128     } else {
129       refElement.accept(new RefVisitor() {
130         @Override public void visitFile(@NotNull RefFile file) {
131           final PsiFile psiFile = file.getElement();
132           if (psiFile != null) {
133             buf.append(B_OPENING);
134             buf.append(psiFile.getName());
135             buf.append(B_CLOSING);
136           }
137         }
138       });
139     }
140   }
141
142   protected void appendQualifiedName(StringBuffer buf, RefEntity refEntity) {
143     if (refEntity == null) return;
144     String qName = "";
145
146     while (!(refEntity instanceof RefProject)) {
147       if (qName.length() > 0) qName = "." + qName;
148
149       String name = null;
150       if (refEntity instanceof RefElement) {
151         final HTMLComposerExtension extension = getLanguageExtension((RefElement)refEntity);
152         if (extension != null) {
153           name = extension.getQualifiedName(refEntity);
154         }
155       }
156
157       if (name == null) {
158         name = refEntity.getName();
159       }
160
161       qName = name + qName;
162       refEntity = refEntity.getOwner();
163     }
164
165     buf.append(qName);
166   }
167
168   @Override
169   public void appendElementReference(final StringBuffer buf, RefElement refElement) {
170     appendElementReference(buf, refElement, true);
171   }
172
173   @Override
174   public void appendElementReference(final StringBuffer buf, RefElement refElement, String linkText, @NonNls String frameName) {
175     if (myExporter == null) {
176       final String url = ((RefElementImpl)refElement).getURL();
177       if (url != null) {
178         appendElementReference(buf, url, linkText, frameName);
179       }
180     }
181     else {
182       appendElementReference(buf, myExporter.getURL(refElement), linkText, frameName);
183     }
184   }
185
186   @Override
187   public void appendElementReference(final StringBuffer buf, String url, String linkText, @NonNls String frameName) {
188     buf.append(A_HREF_OPENING);
189     buf.append(url);
190     if (frameName != null) {
191       @NonNls final String target = "\" target=\"";
192       buf.append(target);
193       buf.append(frameName);
194     }
195
196     buf.append("\">");
197     buf.append(linkText);
198     buf.append(A_CLOSING);
199   }
200
201   protected void appendQuickFix(@NonNls final StringBuffer buf, String text) {
202     if (myExporter == null) {
203       buf.append(text);
204     }
205   }
206
207   @Override
208   public void appendElementReference(final StringBuffer buf, RefElement refElement, boolean isPackageIncluded) {
209     final HTMLComposerExtension extension = getLanguageExtension(refElement);
210
211     if (extension != null) {
212       extension.appendReferencePresentation(refElement, buf, isPackageIncluded);
213     } else if (refElement instanceof RefFile) {
214       buf.append(A_HREF_OPENING);
215
216       if (myExporter == null) {
217         buf.append(((RefElementImpl)refElement).getURL());
218       }
219       else {
220         buf.append(myExporter.getURL(refElement));
221       }
222
223       buf.append("\">");
224       String refElementName = refElement.getName();
225       final PsiElement element = refElement.getElement();
226       if (element != null) {
227         final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element);
228         if (virtualFile != null) {
229           refElementName = ProjectUtilCore.displayUrlRelativeToProject(virtualFile, virtualFile.getPresentableUrl(), element.getProject(),
230                                                                        true, false);
231         }
232       }
233       buf.append(refElementName);
234       buf.append(A_CLOSING);
235     }
236   }
237
238   @Override
239   public String composeNumereables(int n, String statement, String singleEnding, String multipleEnding) {
240     final StringBuilder buf = new StringBuilder();
241     buf.append(n);
242     buf.append(' ');
243     buf.append(statement);
244
245     if (n % 10 == 1 && n % 100 != 11) {
246       buf.append(singleEnding);
247     }
248     else {
249       buf.append(multipleEnding);
250     }
251     return buf.toString();
252   }
253
254   @Override
255   public void appendElementInReferences(StringBuffer buf, RefElement refElement) {
256     if (refElement.getInReferences().size() > 0) {
257       appendHeading(buf, InspectionsBundle.message("inspection.export.results.used.from"));
258       startList(buf);
259       for (RefElement refCaller : refElement.getInReferences()) {
260         appendListItem(buf, refCaller);
261       }
262       doneList(buf);
263     }
264   }
265
266   @Override
267   public void appendElementOutReferences(StringBuffer buf, RefElement refElement) {
268     if (refElement.getOutReferences().size() > 0) {
269       appendHeading(buf, InspectionsBundle.message("inspection.export.results.uses"));
270       startList(buf);
271       for (RefElement refCallee : refElement.getOutReferences()) {
272         appendListItem(buf, refCallee);
273       }
274       doneList(buf);
275     }
276   }
277
278   @Override
279   public void appendListItem(StringBuffer buf, RefElement refElement) {
280     startListItem(buf);
281     appendElementReference(buf, refElement, true);
282     appendAdditionalListItemInfo(buf, refElement);
283     doneListItem(buf);
284   }
285
286   protected void appendAdditionalListItemInfo(StringBuffer buf, RefElement refElement) {
287     // Default appends nothing.
288   }
289
290   protected void appendResolution(StringBuffer buf, RefEntity where, String[] quickFixes) {
291     if (myExporter != null) return;
292     if (where instanceof RefElement && !where.isValid()) return;
293     if (quickFixes != null) {
294       boolean listStarted = false;
295       for (final String text : quickFixes) {
296         if (text == null) continue;
297         if (!listStarted) {
298           appendHeading(buf, InspectionsBundle.message("inspection.problem.resolution"));
299           startList(buf);
300           listStarted = true;
301         }
302         startListItem(buf);
303         appendQuickFix(buf, text);
304         doneListItem(buf);
305       }
306
307       if (listStarted) {
308         doneList(buf);
309       }
310     }
311   }
312
313
314   @Override
315   public void startList(@NonNls final StringBuffer buf) {
316     if (myListStackTop == -1) {
317       buf.append("<div class=\"problem-description\">");
318     }
319     buf.append("<ul>");
320     myListStackTop++;
321     myListStack[myListStackTop] = 0;
322   }
323
324   @Override
325   public void doneList(@NonNls StringBuffer buf) {
326     buf.append("</ul>");
327     if (myListStack[myListStackTop] != 0) {
328       buf.append("<table cellpadding=\"0\" border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr></table>");
329     }
330     if (myListStackTop == 0) {
331       buf.append("</div>");
332     }
333     myListStackTop--;
334   }
335
336   @Override
337   public void startListItem(@NonNls StringBuffer buf) {
338     myListStack[myListStackTop]++;
339     buf.append("<li>");
340   }
341
342   public static void doneListItem(@NonNls StringBuffer buf) {
343     buf.append("</li>");
344   }
345
346   @Override
347   public void appendNoProblems(StringBuffer buf) {
348     buf.append("<p class=\"problem-description-group\">");;
349     buf.append(InspectionsBundle.message("inspection.export.results.no.problems.found"));
350     buf.append("</p>");
351   }
352
353   @Override
354   public <T> T getExtension(final Key<T> key) {
355     return (T)myExtensions.get(key);
356   }
357 }