d50063d77a90f6ea78d7b8831ecfe1b0f47ea7c6
[idea/community.git] / java / compiler / impl / src / com / intellij / compiler / impl / CompilerEncodingServiceImpl.java
1 /*
2  * Copyright 2000-2012 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 package com.intellij.compiler.impl;
17
18 import com.intellij.compiler.CompilerEncodingService;
19 import com.intellij.openapi.compiler.CompilerManager;
20 import com.intellij.openapi.module.Module;
21 import com.intellij.openapi.module.ModuleManager;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.roots.ModuleRootManager;
24 import com.intellij.openapi.roots.ProjectFileIndex;
25 import com.intellij.openapi.roots.ProjectRootManager;
26 import com.intellij.openapi.vfs.VirtualFile;
27 import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
28 import com.intellij.openapi.vfs.encoding.EncodingProjectManagerImpl;
29 import com.intellij.psi.util.CachedValue;
30 import com.intellij.psi.util.CachedValueProvider;
31 import com.intellij.psi.util.CachedValuesManager;
32 import com.intellij.util.containers.ContainerUtil;
33 import gnu.trove.THashMap;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36
37 import java.nio.charset.Charset;
38 import java.util.Collection;
39 import java.util.LinkedHashSet;
40 import java.util.Map;
41 import java.util.Set;
42
43 /**
44  * @author nik
45  */
46 public class CompilerEncodingServiceImpl extends CompilerEncodingService {
47   @NotNull private final Project myProject;
48   private final CachedValue<Map<Module, Set<Charset>>> myModuleFileEncodings;
49
50   public CompilerEncodingServiceImpl(@NotNull Project project) {
51     myProject = project;
52     myModuleFileEncodings = CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider<Map<Module, Set<Charset>>>() {
53       @Override
54       public Result<Map<Module, Set<Charset>>> compute() {
55         Map<Module, Set<Charset>> result = computeModuleCharsetMap();
56         return Result.create(result, ProjectRootManager.getInstance(myProject),
57                              ((EncodingProjectManagerImpl)EncodingProjectManager.getInstance(myProject)).getModificationTracker());
58       }
59     }, false);
60   }
61
62   private Map<Module, Set<Charset>> computeModuleCharsetMap() {
63     final Map<Module, Set<Charset>> map = new THashMap<Module, Set<Charset>>();
64     final Map<VirtualFile, Charset> mappings = EncodingProjectManager.getInstance(myProject).getAllMappings();
65     ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex();
66     final CompilerManager compilerManager = CompilerManager.getInstance(myProject);
67     for (Map.Entry<VirtualFile, Charset> entry : mappings.entrySet()) {
68       final VirtualFile file = entry.getKey();
69       final Charset charset = entry.getValue();
70       if (file == null || charset == null || (!file.isDirectory() && !compilerManager.isCompilableFileType(file.getFileType()))
71           || !index.isInSourceContent(file)) continue;
72
73       final Module module = index.getModuleForFile(file);
74       if (module == null) continue;
75
76       Set<Charset> set = map.get(module);
77       if (set == null) {
78         set = new LinkedHashSet<Charset>();
79         map.put(module, set);
80
81         final VirtualFile sourceRoot = index.getSourceRootForFile(file);
82         VirtualFile current = file.getParent();
83         Charset parentCharset = null;
84         while (current != null) {
85           final Charset currentCharset = mappings.get(current);
86           if (currentCharset != null) {
87             parentCharset = currentCharset;
88           }
89           if (current.equals(sourceRoot)) {
90             break;
91           }
92           current = current.getParent();
93         }
94         if (parentCharset != null) {
95           set.add(parentCharset);
96         }
97       }
98       set.add(charset);
99     }
100     //todo[nik,jeka] perhaps we should take into account encodings of source roots only not individual files
101     for (Module module : ModuleManager.getInstance(myProject).getModules()) {
102       for (VirtualFile file : ModuleRootManager.getInstance(module).getSourceRoots(true)) {
103         Charset encoding = EncodingProjectManager.getInstance(myProject).getEncoding(file, true);
104         if (encoding != null) {
105           Set<Charset> charsets = map.get(module);
106           if (charsets == null) {
107             charsets = new LinkedHashSet<Charset>();
108             map.put(module, charsets);
109           }
110           charsets.add(encoding);
111         }
112       }
113     }
114     
115     return map;
116   }
117
118   @Override
119   @Nullable
120   public Charset getPreferredModuleEncoding(@NotNull Module module) {
121     final Set<Charset> encodings = myModuleFileEncodings.getValue().get(module);
122     return ContainerUtil.getFirstItem(encodings, EncodingProjectManager.getInstance(myProject).getDefaultCharset());
123   }
124
125   @NotNull
126   @Override
127   public Collection<Charset> getAllModuleEncodings(@NotNull Module module) {
128     final Set<Charset> encodings = myModuleFileEncodings.getValue().get(module);
129     if (encodings != null) {
130       return encodings;
131     }
132     return ContainerUtil.createMaybeSingletonList(EncodingProjectManager.getInstance(myProject).getDefaultCharset());
133   }
134 }