df60f60bd17eab7534ab192f91242a3f205761ce
[idea/community.git] / plugins / properties / properties-psi-impl / src / com / intellij / lang / properties / editor / ResourceBundleFileStructureViewElement.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  * @author Alexey
19  */
20 package com.intellij.lang.properties.editor;
21
22 import com.intellij.icons.AllIcons;
23 import com.intellij.ide.structureView.StructureViewTreeElement;
24 import com.intellij.lang.properties.IProperty;
25 import com.intellij.lang.properties.ResourceBundle;
26 import com.intellij.lang.properties.psi.PropertiesFile;
27 import com.intellij.navigation.ItemPresentation;
28 import com.intellij.psi.PsiFile;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.containers.HashSet;
31 import com.intellij.util.containers.MultiMap;
32 import gnu.trove.TObjectIntHashMap;
33 import gnu.trove.TObjectIntProcedure;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36
37 import javax.swing.*;
38 import java.util.Collection;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.concurrent.ConcurrentMap;
42
43 public class ResourceBundleFileStructureViewElement implements StructureViewTreeElement, ResourceBundleEditorViewElement {
44   private final ResourceBundle myResourceBundle;
45
46   private volatile boolean myShowOnlyIncomplete;
47   private final ConcurrentMap<String, ResourceBundlePropertyStructureViewElement> myElements = ContainerUtil.newConcurrentMap();
48
49   public ResourceBundleFileStructureViewElement(final ResourceBundle resourceBundle) {
50     myResourceBundle = resourceBundle;
51   }
52
53   public void setShowOnlyIncomplete(boolean showOnlyIncomplete) {
54     myShowOnlyIncomplete = showOnlyIncomplete;
55   }
56
57   public boolean isShowOnlyIncomplete() {
58     return myShowOnlyIncomplete;
59   }
60
61   @Override
62   public ResourceBundle getValue() {
63     return myResourceBundle;
64   }
65
66   @NotNull
67   public synchronized StructureViewTreeElement[] getChildren() {
68     final MultiMap<String, IProperty> propertyNames = getPropertiesMap(myResourceBundle, myShowOnlyIncomplete);
69
70     final HashSet<String> remains = new HashSet<>(myElements.keySet());
71     for (Map.Entry<String, Collection<IProperty>> entry : propertyNames.entrySet()) {
72       final String propKey = entry.getKey();
73       final IProperty representative = entry.getValue().iterator().next();
74       final ResourceBundlePropertyStructureViewElement oldPropertyNode = myElements.get(propKey);
75       if (oldPropertyNode != null && oldPropertyNode.getProperty() == representative) {
76         remains.remove(propKey);
77         continue;
78       }
79       final ResourceBundlePropertyStructureViewElement node = new ResourceBundlePropertyStructureViewElement(representative);
80       myElements.put(propKey, node);
81     }
82
83     for (String remain : remains) {
84       myElements.remove(remain);
85     }
86
87     return myElements.values().toArray(new StructureViewTreeElement[myElements.size()]);
88   }
89
90   public static MultiMap<String, IProperty> getPropertiesMap(ResourceBundle resourceBundle, boolean onlyIncomplete) {
91     List<PropertiesFile> propertiesFiles = resourceBundle.getPropertiesFiles();
92     final MultiMap<String, IProperty> propertyNames;
93     if (onlyIncomplete) {
94       propertyNames = getChildrenIdShowOnlyIncomplete(resourceBundle);
95     } else {
96       propertyNames = MultiMap.createLinked();
97       for (PropertiesFile propertiesFile : propertiesFiles) {
98         List<IProperty> properties = propertiesFile.getProperties();
99         for (IProperty property : properties) {
100           String name = property.getKey();
101           propertyNames.putValue(name, property);
102         }
103       }
104     }
105     return propertyNames;
106   }
107
108   private static MultiMap<String, IProperty> getChildrenIdShowOnlyIncomplete(ResourceBundle resourceBundle) {
109     final MultiMap<String, IProperty> propertyNames = MultiMap.createLinked();
110     TObjectIntHashMap<String> occurrences = new TObjectIntHashMap<>();
111     for (PropertiesFile file : resourceBundle.getPropertiesFiles()) {
112       MultiMap<String, IProperty> currentFilePropertyNames = MultiMap.createLinked();
113       for (IProperty property : file.getProperties()) {
114         String name = property.getKey();
115         currentFilePropertyNames.putValue(name, property);
116       }
117       propertyNames.putAllValues(currentFilePropertyNames);
118       for (String propertyName : currentFilePropertyNames.keySet()) {
119         if (occurrences.contains(propertyName)) {
120           occurrences.adjustValue(propertyName, 1);
121         }
122         else {
123           occurrences.put(propertyName, 1);
124         }
125       }
126     }
127     final int targetOccurrences = resourceBundle.getPropertiesFiles().size();
128     occurrences.forEachEntry(new TObjectIntProcedure<String>() {
129       @Override
130       public boolean execute(String propertyName, int occurrences) {
131         if (occurrences == targetOccurrences) {
132           propertyNames.remove(propertyName);
133         }
134         return true;
135       }
136     });
137     return propertyNames;
138   }
139
140   @NotNull
141   public ItemPresentation getPresentation() {
142     return new ItemPresentation() {
143       public String getPresentableText() {
144         return myResourceBundle.getBaseName();
145       }
146
147       public String getLocationString() {
148         return null;
149       }
150
151       public Icon getIcon(boolean open) {
152         return AllIcons.FileTypes.Properties;
153       }
154     };
155   }
156
157   @Nullable
158   @Override
159   public IProperty[] getProperties() {
160     return new IProperty[0];
161   }
162
163   @Nullable
164   @Override
165   public PsiFile[] getFiles() {
166     final List<PropertiesFile> files = getValue().getPropertiesFiles();
167     return ContainerUtil.map2Array(files, new PsiFile[files.size()], propertiesFile -> propertiesFile.getContainingFile());
168   }
169
170   public void navigate(boolean requestFocus) {
171
172   }
173
174   public boolean canNavigate() {
175     return false;
176   }
177
178   public boolean canNavigateToSource() {
179     return false;
180   }
181 }