export html action: location + name merged
[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, int index) {
202     if (myExporter == null) {
203       buf.append("<a HREF=\"file://bred.txt#invoke:").append(index);
204       buf.append("\">");
205       buf.append(text);
206       buf.append("</a>");
207     }
208   }
209
210   @Override
211   public void appendElementReference(final StringBuffer buf, RefElement refElement, boolean isPackageIncluded) {
212     final HTMLComposerExtension extension = getLanguageExtension(refElement);
213
214     if (extension != null) {
215       extension.appendReferencePresentation(refElement, buf, isPackageIncluded);
216     } else if (refElement instanceof RefFile) {
217       buf.append(A_HREF_OPENING);
218
219       if (myExporter == null) {
220         buf.append(((RefElementImpl)refElement).getURL());
221       }
222       else {
223         buf.append(myExporter.getURL(refElement));
224       }
225
226       buf.append("\">");
227       String refElementName = refElement.getName();
228       final PsiElement element = refElement.getElement();
229       if (element != null) {
230         final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element);
231         if (virtualFile != null) {
232           refElementName = ProjectUtilCore.displayUrlRelativeToProject(virtualFile, virtualFile.getPresentableUrl(), element.getProject(),
233                                                                        true, false);
234         }
235       }
236       buf.append(refElementName);
237       buf.append(A_CLOSING);
238     }
239   }
240
241   @Override
242   public String composeNumereables(int n, String statement, String singleEnding, String multipleEnding) {
243     final StringBuilder buf = new StringBuilder();
244     buf.append(n);
245     buf.append(' ');
246     buf.append(statement);
247
248     if (n % 10 == 1 && n % 100 != 11) {
249       buf.append(singleEnding);
250     }
251     else {
252       buf.append(multipleEnding);
253     }
254     return buf.toString();
255   }
256
257   @Override
258   public void appendElementInReferences(StringBuffer buf, RefElement refElement) {
259     if (refElement.getInReferences().size() > 0) {
260       appendHeading(buf, InspectionsBundle.message("inspection.export.results.used.from"));
261       startList(buf);
262       for (RefElement refCaller : refElement.getInReferences()) {
263         appendListItem(buf, refCaller);
264       }
265       doneList(buf);
266     }
267   }
268
269   @Override
270   public void appendElementOutReferences(StringBuffer buf, RefElement refElement) {
271     if (refElement.getOutReferences().size() > 0) {
272       appendHeading(buf, InspectionsBundle.message("inspection.export.results.uses"));
273       startList(buf);
274       for (RefElement refCallee : refElement.getOutReferences()) {
275         appendListItem(buf, refCallee);
276       }
277       doneList(buf);
278     }
279   }
280
281   @Override
282   public void appendListItem(StringBuffer buf, RefElement refElement) {
283     startListItem(buf);
284     appendElementReference(buf, refElement, true);
285     appendAdditionalListItemInfo(buf, refElement);
286     doneListItem(buf);
287   }
288
289   protected void appendAdditionalListItemInfo(StringBuffer buf, RefElement refElement) {
290     // Default appends nothing.
291   }
292
293   protected void appendResolution(StringBuffer buf, RefEntity where, String[] quickFixes) {
294     if (myExporter != null) return;
295     if (where instanceof RefElement && !where.isValid()) return;
296     if (quickFixes != null) {
297       boolean listStarted = false;
298       for (int i = 0; i < quickFixes.length; i++) {
299         final String text = quickFixes[i];
300         if (text == null) continue;
301         if (!listStarted) {
302           appendHeading(buf, InspectionsBundle.message("inspection.problem.resolution"));
303           startList(buf);
304           listStarted = true;
305         }
306         startListItem(buf);
307         appendQuickFix(buf, text, i);
308         doneListItem(buf);
309       }
310
311       if (listStarted) {
312         doneList(buf);
313       }
314     }
315   }
316
317
318   @Override
319   public void startList(@NonNls final StringBuffer buf) {
320     if (myListStackTop == -1) {
321       buf.append("<div class=\"problem-description\">");
322     }
323     buf.append("<ul>");
324     myListStackTop++;
325     myListStack[myListStackTop] = 0;
326   }
327
328   @Override
329   public void doneList(@NonNls StringBuffer buf) {
330     buf.append("</ul>");
331     if (myListStack[myListStackTop] != 0) {
332       buf.append("<table cellpadding=\"0\" border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr></table>");
333     }
334     if (myListStackTop == 0) {
335       buf.append("</div>");
336     }
337     myListStackTop--;
338   }
339
340   @Override
341   public void startListItem(@NonNls StringBuffer buf) {
342     myListStack[myListStackTop]++;
343     buf.append("<li>");
344   }
345
346   public static void doneListItem(@NonNls StringBuffer buf) {
347     buf.append("</li>");
348   }
349
350   @Override
351   public void appendNoProblems(StringBuffer buf) {
352     buf.append("<p class=\"problem-description-group\">");;
353     buf.append(InspectionsBundle.message("inspection.export.results.no.problems.found"));
354     buf.append("</p>");
355   }
356
357   @Override
358   public <T> T getExtension(final Key<T> key) {
359     return (T)myExtensions.get(key);
360   }
361 }