EA-31156 - NPE: HTMLComposerImpl$1.visitFile
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInspection / ex / HTMLComposerImpl.java
1 /*
2  * Copyright 2000-2009 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.util.Key;
37 import com.intellij.psi.PsiElement;
38 import com.intellij.psi.PsiFile;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.Nullable;
41
42 import java.net.URL;
43 import java.util.HashMap;
44 import java.util.Map;
45
46 /**
47  * @author max
48  */
49 public abstract class HTMLComposerImpl extends HTMLComposer {
50   protected HTMLExporter myExporter;
51   private final int[] myListStack;
52   private int myListStackTop;
53   private final Map<Key, HTMLComposerExtension> myExtensions = new HashMap<Key, HTMLComposerExtension>();
54   private final Map<Language, HTMLComposerExtension> myLanguageExtensions = new HashMap<Language, HTMLComposerExtension>();
55   @NonNls protected static final String BR = "<br>";
56   @NonNls protected static final String NBSP = "&nbsp;";
57   @NonNls protected static final String CODE_CLOSING = "</code>";
58   @NonNls protected static final String CODE_OPENING = "<code>";
59   @NonNls protected static final String FONT_CLOSING = "</font>";
60   @NonNls protected static final String B_OPENING = "<b>";
61   @NonNls protected static final String B_CLOSING = "</b>";
62   @NonNls protected static final String FONT_OPENING = "<font style=\"font-family:verdana;";
63   @NonNls protected static final String CLOSE_TAG = "\">";
64   @NonNls protected static final String A_HREF_OPENING = "<a HREF=\"";
65   @NonNls protected static final String A_CLOSING = "</a>";
66
67   protected HTMLComposerImpl() {
68     myListStack = new int[5];
69     myListStackTop = -1;
70     for (InspectionExtensionsFactory factory : Extensions.getExtensions(InspectionExtensionsFactory.EP_NAME)) {
71       final HTMLComposerExtension extension = factory.createHTMLComposerExtension(this);
72       if (extension != null) {
73         myExtensions.put(extension.getID(), extension);
74         myLanguageExtensions.put(extension.getLanguage(), extension);
75       }
76     }
77   }
78
79   public abstract void compose(StringBuffer buf, RefEntity refEntity);
80
81   public void compose(StringBuffer buf, RefEntity refElement, CommonProblemDescriptor descriptor) {}
82
83   public void composeWithExporter(StringBuffer buf, RefEntity refEntity, HTMLExporter exporter) {
84     myExporter = exporter;
85     compose(buf, refEntity);
86     myExporter = null;
87   }
88
89   protected void genPageHeader(final StringBuffer buf, RefEntity refEntity) {
90     if (refEntity instanceof RefElement) {
91       RefElement refElement = (RefElement)refEntity;
92
93       appendHeading(buf, InspectionsBundle.message("inspection.offline.view.tool.display.name.title"));
94       buf.append(BR);
95       appendAfterHeaderIndention(buf);
96
97       appendShortName(buf, refElement);
98       buf.append(BR).append(BR);
99
100       appendHeading(buf, InspectionsBundle.message("inspection.export.results.capitalized.location"));
101       buf.append(BR);
102       appendAfterHeaderIndention(buf);
103       appendLocation(buf, refElement);
104       buf.append(BR).append(BR);
105     }
106   }
107
108   private void appendLocation(final StringBuffer buf, final RefElement refElement) {
109     buf.append(FONT_OPENING);
110     buf.append(CLOSE_TAG);
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     buf.append(FONT_CLOSING);
121   }
122
123   @Nullable
124   private HTMLComposerExtension getLanguageExtension(final RefElement refElement) {
125     final PsiElement element = refElement.getElement();
126     return element != null ? myLanguageExtensions.get(element.getLanguage()) : null;
127   }
128
129   private void appendShortName(final StringBuffer buf, RefElement refElement) {
130     final HTMLComposerExtension extension = getLanguageExtension(refElement);
131     if (extension != null) {
132       extension.appendShortName(refElement, buf);
133     }
134     refElement.accept(new RefVisitor() {
135       @Override public void visitFile(RefFile file) {
136         final PsiFile psiFile = file.getElement();
137         if (psiFile != null) {
138           buf.append(B_OPENING);
139           buf.append(psiFile.getName());
140           buf.append(B_CLOSING);
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 = refEntity.getName();
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       qName = name + qName;
162       refEntity = refEntity.getOwner();
163     }
164
165     buf.append(qName);
166   }
167
168   public void appendElementReference(final StringBuffer buf, RefElement refElement) {
169     appendElementReference(buf, refElement, true);
170   }
171
172   public void appendElementReference(final StringBuffer buf, RefElement refElement, String linkText, @NonNls String frameName) {    
173     if (myExporter == null) {
174       final URL url = ((RefElementImpl)refElement).getURL();
175       if (url != null) {
176         appendElementReference(buf, url.toString(), linkText, frameName);
177       }
178     }
179     else {
180       appendElementReference(buf, myExporter.getURL(refElement), linkText, frameName);
181     }
182   }
183
184   public void appendElementReference(final StringBuffer buf, String url, String linkText, @NonNls String frameName) {
185     buf.append(A_HREF_OPENING);
186     buf.append(url);
187     if (frameName != null) {
188       @NonNls final String target = "\" target=\"";
189       buf.append(target);
190       buf.append(frameName);
191     }
192
193     buf.append("\">");
194     buf.append(linkText);
195     buf.append(A_CLOSING);
196   }
197
198   protected void appendQuickFix(@NonNls final StringBuffer buf, String text, int index) {
199     if (myExporter == null) {
200       buf.append(FONT_OPENING);
201       buf.append(CLOSE_TAG);
202       buf.append("<a HREF=\"file://bred.txt#invoke:").append(index);
203       buf.append("\">");
204       buf.append(text);
205       buf.append("</a></font>");
206     }
207   }
208
209   public void appendElementReference(final StringBuffer buf, RefElement refElement, boolean isPackageIncluded) {
210     final HTMLComposerExtension extension = getLanguageExtension(refElement);
211     
212     if (extension != null) {
213       extension.appendReferencePresentation(refElement, buf, isPackageIncluded);
214     } else if (refElement instanceof RefFile) {
215       buf.append(HTMLComposerImpl.A_HREF_OPENING);
216
217       if (myExporter == null) {
218         buf.append(((RefElementImpl)refElement).getURL());
219       }
220       else {
221         buf.append(myExporter.getURL(refElement));
222       }
223
224       buf.append("\">");
225       buf.append(refElement.getName());
226       buf.append(HTMLComposerImpl.A_CLOSING);
227     }
228   }
229
230   public String composeNumereables(int n, String statement, String singleEnding, String multipleEnding) {
231     final StringBuffer buf = new StringBuffer();
232     buf.append(n);
233     buf.append(' ');
234     buf.append(statement);
235
236     if (n % 10 == 1 && n % 100 != 11) {
237       buf.append(singleEnding);
238     }
239     else {
240       buf.append(multipleEnding);
241     }
242     return buf.toString();
243   }
244
245   public void appendElementInReferences(StringBuffer buf, RefElement refElement) {
246     if (refElement.getInReferences().size() > 0) {
247       appendHeading(buf, InspectionsBundle.message("inspection.export.results.used.from"));
248       startList(buf);
249       for (RefElement refCaller : refElement.getInReferences()) {
250         appendListItem(buf, refCaller);
251       }
252       doneList(buf);
253     }
254   }
255
256   public void appendElementOutReferences(StringBuffer buf, RefElement refElement) {
257     if (refElement.getOutReferences().size() > 0) {
258       buf.append(BR);
259       appendHeading(buf, InspectionsBundle.message("inspection.export.results.uses"));
260       startList(buf);
261       for (RefElement refCallee : refElement.getOutReferences()) {
262         appendListItem(buf, refCallee);
263       }
264       doneList(buf);
265     }
266   }
267
268   public void appendListItem(StringBuffer buf, RefElement refElement) {
269     startListItem(buf);
270     buf.append(FONT_OPENING);
271     buf.append(CLOSE_TAG);
272     appendElementReference(buf, refElement, true);
273     appendAdditionalListItemInfo(buf, refElement);
274     buf.append(FONT_CLOSING);
275     doneListItem(buf);
276   }
277
278   protected void appendAdditionalListItemInfo(StringBuffer buf, RefElement refElement) {
279     // Default appends nothing.
280   }
281
282   protected void appendResolution(StringBuffer buf, InspectionTool tool, RefEntity where) {
283     if (myExporter != null) return;
284     if (where instanceof RefElement && !where.isValid()) return;
285     QuickFixAction[] quickFixes = tool.getQuickFixes(new RefEntity[] {where});
286     if (quickFixes != null) {
287       boolean listStarted = false;
288       for (int i = 0; i < quickFixes.length; i++) {
289         QuickFixAction quickFix = quickFixes[i];
290         final String text = quickFix.getText(where);
291         if (text == null) continue;
292         if (!listStarted) {
293           appendHeading(buf, InspectionsBundle.message("inspection.problem.resolution"));
294           startList(buf);
295           listStarted = true;
296         }
297         startListItem(buf);
298         appendQuickFix(buf, text, i);
299         doneListItem(buf);
300       }
301
302       if (listStarted) {
303         doneList(buf);
304       }
305     }
306   }
307
308   public void startList(@NonNls final StringBuffer buf) {
309     buf.append("<ul>");
310     myListStackTop++;
311     myListStack[myListStackTop] = 0;
312   }
313
314   public void doneList(@NonNls StringBuffer buf) {
315     buf.append("</ul>");
316     if (myListStack[myListStackTop] != 0) {
317       buf.append("<table cellpadding=\"0\" border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr></table>");
318     }
319     myListStackTop--;
320   }
321
322   public void startListItem(@NonNls StringBuffer buf) {
323     myListStack[myListStackTop]++;
324     buf.append("<li>");
325   }
326
327   public static void doneListItem(@NonNls StringBuffer buf) {
328     buf.append("</li>");
329   }
330
331   public void appendNoProblems(StringBuffer buf) {
332     buf.append(BR);
333     appendAfterHeaderIndention(buf);
334     buf.append(B_OPENING);
335     buf.append(InspectionsBundle.message("inspection.export.results.no.problems.found"));
336     buf.append(B_CLOSING).append(BR);
337   }
338
339   public <T> T getExtension(final Key<T> key) {
340     return (T)myExtensions.get(key);
341   }
342 }