2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 package com.intellij.lang.properties.editor;
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;
38 import java.util.Collection;
39 import java.util.List;
41 import java.util.concurrent.ConcurrentMap;
43 public class ResourceBundleFileStructureViewElement implements StructureViewTreeElement, ResourceBundleEditorViewElement {
44 private final ResourceBundle myResourceBundle;
46 private volatile boolean myShowOnlyIncomplete;
47 private final ConcurrentMap<String, ResourceBundlePropertyStructureViewElement> myElements = ContainerUtil.newConcurrentMap();
49 public ResourceBundleFileStructureViewElement(final ResourceBundle resourceBundle) {
50 myResourceBundle = resourceBundle;
53 public void setShowOnlyIncomplete(boolean showOnlyIncomplete) {
54 myShowOnlyIncomplete = showOnlyIncomplete;
57 public boolean isShowOnlyIncomplete() {
58 return myShowOnlyIncomplete;
62 public ResourceBundle getValue() {
63 return myResourceBundle;
67 public synchronized StructureViewTreeElement[] getChildren() {
68 final MultiMap<String, IProperty> propertyNames = getPropertiesMap(myResourceBundle, myShowOnlyIncomplete);
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);
79 final ResourceBundlePropertyStructureViewElement node = new ResourceBundlePropertyStructureViewElement(representative);
80 myElements.put(propKey, node);
83 for (String remain : remains) {
84 myElements.remove(remain);
87 return myElements.values().toArray(new StructureViewTreeElement[myElements.size()]);
90 public static MultiMap<String, IProperty> getPropertiesMap(ResourceBundle resourceBundle, boolean onlyIncomplete) {
91 List<PropertiesFile> propertiesFiles = resourceBundle.getPropertiesFiles();
92 final MultiMap<String, IProperty> propertyNames;
94 propertyNames = getChildrenIdShowOnlyIncomplete(resourceBundle);
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);
105 return propertyNames;
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);
117 propertyNames.putAllValues(currentFilePropertyNames);
118 for (String propertyName : currentFilePropertyNames.keySet()) {
119 if (occurrences.contains(propertyName)) {
120 occurrences.adjustValue(propertyName, 1);
123 occurrences.put(propertyName, 1);
127 final int targetOccurrences = resourceBundle.getPropertiesFiles().size();
128 occurrences.forEachEntry(new TObjectIntProcedure<String>() {
130 public boolean execute(String propertyName, int occurrences) {
131 if (occurrences == targetOccurrences) {
132 propertyNames.remove(propertyName);
137 return propertyNames;
141 public ItemPresentation getPresentation() {
142 return new ItemPresentation() {
143 public String getPresentableText() {
144 return myResourceBundle.getBaseName();
147 public String getLocationString() {
151 public Icon getIcon(boolean open) {
152 return AllIcons.FileTypes.Properties;
159 public IProperty[] getProperties() {
160 return new IProperty[0];
165 public PsiFile[] getFiles() {
166 final List<PropertiesFile> files = getValue().getPropertiesFiles();
167 return ContainerUtil.map2Array(files, new PsiFile[files.size()], propertiesFile -> propertiesFile.getContainingFile());
170 public void navigate(boolean requestFocus) {
174 public boolean canNavigate() {
178 public boolean canNavigateToSource() {