force running "main" tools in global context
[idea/community.git] / plugins / copyright / src / com / maddyhome / idea / copyright / util / FileTypeUtil.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 package com.maddyhome.idea.copyright.util;
18
19 import com.intellij.lang.Commenter;
20 import com.intellij.lang.LanguageCommenters;
21 import com.intellij.openapi.components.ServiceManager;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.fileTypes.*;
24 import com.intellij.openapi.project.ProjectUtil;
25 import com.intellij.openapi.util.text.StringUtil;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.psi.PsiDirectory;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.util.messages.MessageBus;
30 import com.maddyhome.idea.copyright.CopyrightUpdaters;
31 import com.maddyhome.idea.copyright.options.LanguageOptions;
32
33 import java.util.*;
34
35 public class FileTypeUtil
36 {
37     public static synchronized FileTypeUtil getInstance()
38     {
39       return ServiceManager.getService(FileTypeUtil.class);
40     }
41
42     public static String buildComment(FileType type, String template, LanguageOptions options)
43     {
44
45       Commenter commenter = getCommenter(type);
46         if (commenter == null)
47         {
48             return "<No comments>";
49         }
50
51         String bs = commenter.getBlockCommentPrefix();
52         String be = commenter.getBlockCommentSuffix();
53         String ls = commenter.getLineCommentPrefix();
54
55         if ((bs == null || be == null) && ls == null)
56         {
57             return "<No comments>";
58         }
59
60         boolean allowBlock = bs != null && be != null;
61         boolean allowLine = ls != null;
62         if (allowLine && !allowBlock)
63         {
64             bs = ls;
65             be = ls;
66         }
67
68         boolean allowSeparator = getInstance().allowSeparators(type);
69         char filler = options.getFiller();
70         if (!allowSeparator)
71         {
72             if (options.getFiller() == LanguageOptions.DEFAULT_FILLER)
73             {
74                 filler = '~';
75             }
76         }
77
78         boolean isBlock = options.isBlock();
79         boolean isPrefix = options.isPrefixLines();
80         if (isBlock && !allowBlock)
81         {
82             isPrefix = true;
83         }
84         boolean isBox = options.isBox() && options.isSeparateBefore() && options.isSeparateAfter() &&
85             options.getLenBefore() == options.getLenAfter();
86
87         StringBuffer preview = new StringBuffer(80);
88         String open = isBlock ? bs : allowLine ? ls : bs;
89         String close = isBlock ? be : allowLine ? ls : be;
90         StringBuffer pre = new StringBuffer(5);
91         StringBuffer leader = new StringBuffer(5);
92         StringBuffer post = new StringBuffer(5);
93         if (filler == LanguageOptions.DEFAULT_FILLER)
94         {
95             filler = open.charAt(open.length() - 1);
96         }
97         int offset = 0;
98         if (isBlock)
99         {
100             int pos = open.length() - 1;
101             pre.append(allowBlock ? filler : open.charAt(pos));
102             while (pos > 0 && open.charAt(pos) == open.charAt(open.length() - 1))
103             {
104                 pos--;
105                 offset++;
106             }
107             while (open.length() > 1 && pos >= 0)
108             {
109                 leader.append(' ');
110                 pos--;
111             }
112             post.append(filler);
113             if (!isPrefix)
114             {
115                 pre = new StringBuffer(0);
116             }
117             if (!allowBlock)
118             {
119                 close = Character.toString(filler);
120             }
121         }
122         else
123         {
124             if (allowLine)
125             {
126                 close = Character.toString(filler);
127             }
128             pre.append(open);
129             post.append(close);
130         }
131
132         int diff = 0;
133         if (options.isSeparateBefore())
134         {
135             if (isBlock && isBox && allowBlock)
136             {
137                 diff = close.length() - offset;
138             }
139
140             preview.append(open);
141             for (int i = open.length() + 1; i <= options.getLenBefore() - diff - post.length(); i++)
142             {
143                 preview.append(filler);
144             }
145
146             preview.append(post);
147
148             preview.append('\n');
149         }
150         else if (isBlock)
151         {
152             preview.append(open).append('\n');
153         }
154
155         if (template.length() > 0)
156         {
157             String[] lines = template.split("\n", -1);
158             for (String line : lines)
159             {
160                 if (options.isTrim()) {
161                   line = line.trim();
162                 }
163               line = StringUtil.trimStart(StringUtil.trimStart(line, pre.toString()), open);
164               line = StringUtil.trimEnd(line, close);
165                 preview.append(leader).append(pre);
166                 int len = 0;
167                 if (pre.length() > 0 && line.length() > 0)
168                 {
169                     preview.append(' ');
170                     len++;
171                 }
172                 preview.append(line);
173                 len += line.length() + leader.length() + pre.length();
174                 if (isBox && len < options.getLenBefore() - diff)
175                 {
176                     for (; len < options.getLenBefore() - diff - post.length(); len++)
177                     {
178                         preview.append(' ');
179                     }
180                     if (isBlock || allowLine)
181                     {
182                         preview.append(post.substring(0, options.getLenBefore() - diff - len));
183                     }
184                 }
185
186                 if (!isBlock && !allowLine)
187                 {
188                     if (preview.charAt(preview.length() - 1) != ' ')
189                     {
190                         preview.append(' ');
191                     }
192                     preview.append(close);
193                 }
194
195                 preview.append('\n');
196             }
197         }
198
199         preview.append(leader);
200         if (options.isSeparateAfter())
201         {
202             preview.append(pre);
203             for (int i = leader.length() + pre.length(); i < options.getLenAfter() - close.length(); i++)
204             {
205                 preview.append(filler);
206             }
207             preview.append(close);
208             preview.append('\n');
209         }
210         else if (isBlock)
211         {
212           if (!allowBlock) {
213             preview.append(pre).append('\n');
214           } else {
215             preview.append(close).append('\n');
216           }
217         }
218
219         return preview.substring(0, preview.length() - 1);
220     }
221
222     public boolean isSupportedFile(VirtualFile file)
223     {
224         if (file == null || file.isDirectory())
225         {
226             return false;
227         }
228
229         if (ProjectUtil.isProjectOrWorkspaceFile(file)) return false;
230       FileType type = file.getFileType();
231
232         return types.get(type.getName()) != null;
233     }
234
235     public static boolean isSupportedFile(PsiFile file)
236     {
237         if (file == null || file instanceof PsiDirectory)
238         {
239             return false;
240         }
241         final VirtualFile virtualFile = file.getVirtualFile();
242         if (virtualFile == null) return false;
243         if (ProjectUtil.isProjectOrWorkspaceFile(virtualFile)) return false;
244         return isSupportedType(virtualFile.getFileType());
245     }
246
247     public FileType[] getSupportedTypes()
248     {
249         return new HashSet<FileType>(types.values()).toArray(new FileType[]{});
250     }
251
252     public FileType getFileTypeByFile(VirtualFile file)
253     {
254       FileType type = file.getFileType();
255
256         return getFileTypeByType(type);
257     }
258
259     public FileType getFileTypeByType(FileType type)
260     {
261         return types.get(type.getName());
262     }
263
264     public String getFileTypeNameByName(String name)
265     {
266         FileType type = types.get(name);
267
268         return type != null ? type.getName() : name;
269     }
270
271     public static boolean hasBlockComment(FileType fileType)
272     {
273         Commenter commenter = getCommenter(fileType);
274
275         return commenter != null && commenter.getBlockCommentPrefix() != null;
276     }
277
278     public static boolean hasLineComment(FileType fileType)
279     {
280         Commenter commenter = getCommenter(fileType);
281
282         return commenter != null && commenter.getLineCommentPrefix() != null;
283     }
284
285     public boolean allowSeparators(FileType fileType)
286     {
287         FileType type = getFileTypeByType(fileType);
288
289         return !noSeparators.contains(type);
290     }
291
292     public FileTypeUtil(MessageBus bus)
293     {
294         createMappings();
295         loadFileTypes();
296         bus.connect().subscribe(FileTypeManager.TOPIC, new FileTypeListener() {
297           @Override
298           public void beforeFileTypesChanged(FileTypeEvent event) {
299           }
300
301           @Override
302           public void fileTypesChanged(FileTypeEvent event) {
303             loadFileTypes();
304           }
305         });
306     }
307
308     private static Commenter getCommenter(FileType fileType)
309     {
310         if (fileType instanceof LanguageFileType)
311         {
312           return LanguageCommenters.INSTANCE.forLanguage(((LanguageFileType) fileType).getLanguage());
313         }
314
315
316         return null;
317     }
318
319     private void createMappings()
320     {
321         Set<FileType> maps = new HashSet<FileType>();
322         maps.add(StdFileTypes.DTD);
323         maps.add(StdFileTypes.XML);
324
325         mappings.put(StdFileTypes.XML, maps);
326
327         maps = new HashSet<FileType>();
328         maps.add(StdFileTypes.HTML);
329         maps.add(StdFileTypes.XHTML);
330
331         mappings.put(StdFileTypes.HTML, maps);
332
333         maps = new HashSet<FileType>();
334         maps.add(StdFileTypes.JSP);
335
336         mappings.put(StdFileTypes.JSP, maps);
337
338         noSeparators.add(StdFileTypes.XML);
339         noSeparators.add(StdFileTypes.HTML);
340         noSeparators.add(StdFileTypes.JSP);
341         noSeparators.add(StdFileTypes.JSPX);
342
343     }
344
345     private static boolean isSupportedType(FileType type)
346     {
347         if (type.isBinary() || type.getName().indexOf("IDEA") >= 0 || "GUI_DESIGNER_FORM".equals(type.getName()))
348         {
349             return false;
350         }
351         else
352         {
353             Commenter commenter = getCommenter(type);
354             boolean hasComment = commenter != null &&
355                 (commenter.getLineCommentPrefix() != null || commenter.getBlockCommentPrefix() != null);
356             if (!hasComment)
357             {
358                 return false;
359             }
360             else
361             {
362                 if (type.equals(StdFileTypes.DTD))
363                 {
364                     return true;
365                 }
366                 else if (type.equals(StdFileTypes.HTML))
367                 {
368                     return true;
369                 }
370                 else if (type.equals(StdFileTypes.XHTML))
371                 {
372                     return true;
373                 }
374                 else if (type.equals(StdFileTypes.PROPERTIES))
375                 {
376                     return true;
377                 }
378                 else if ("JavaScript".equals(type.getName()))
379                 {
380                     return true;
381                 }
382             }
383             return CopyrightUpdaters.INSTANCE.forFileType(type) != null;
384         }
385     }
386
387     private void loadFileTypes()
388     {
389         logger.debug("loadFileTypes");
390         types.clear();
391         FileType[] ftypes = FileTypeManager.getInstance().getRegisteredFileTypes();
392         for (FileType ftype : ftypes)
393         {
394             // Ignore binary files
395             // Ignore IDEA specific file types (PROJECT, MODULE, WORKSPACE)
396             // Ignore GUI Designer files
397             if (isSupportedType(ftype))
398             {
399                 logger.debug("adding " + ftype.getName());
400                 Iterator<FileType> iter = mappings.keySet().iterator();
401                 FileType type = ftype;
402                 while (iter.hasNext())
403                 {
404                     FileType fileType = iter.next();
405                     Set<FileType> maps = mappings.get(fileType);
406                     if (maps.contains(ftype))
407                     {
408                         type = fileType;
409                         break;
410                     }
411                 }
412                 types.put(ftype.getName(), type);
413             }
414             else
415             {
416                 logger.debug("ignoring " + ftype.getName());
417             }
418         }
419     }
420
421   public FileType getFileTypeByName(String name) {
422     return types.get(name);
423   }
424
425   public static class SortByName implements Comparator<FileType>
426     {
427         public int compare(FileType a, FileType b)
428         {
429             return a.getName().compareToIgnoreCase(b.getName());
430         }
431     }
432
433     private final Map<String, FileType> types = new HashMap<String, FileType>();
434     private final Map<FileType, Set<FileType>> mappings = new HashMap<FileType, Set<FileType>>();
435     private final Set<FileType> noSeparators = new HashSet<FileType>();
436
437     private static final Logger logger = Logger.getInstance(FileTypeUtil.class.getName());
438 }