fix "IDEA-221944 Deadlock on opening second project" and support preloading for proje...
[idea/community.git] / plugins / ui-designer / src / com / intellij / uiDesigner / StringDescriptorManager.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2
3 package com.intellij.uiDesigner;
4
5 import com.intellij.ProjectTopics;
6 import com.intellij.lang.properties.IProperty;
7 import com.intellij.lang.properties.PropertiesUtilBase;
8 import com.intellij.lang.properties.psi.PropertiesFile;
9 import com.intellij.openapi.module.Module;
10 import com.intellij.openapi.module.ModuleServiceManager;
11 import com.intellij.openapi.roots.ModuleRootEvent;
12 import com.intellij.openapi.roots.ModuleRootListener;
13 import com.intellij.openapi.util.Pair;
14 import com.intellij.uiDesigner.lw.StringDescriptor;
15 import com.intellij.uiDesigner.radComponents.RadComponent;
16 import com.intellij.uiDesigner.radComponents.RadRootContainer;
17 import com.intellij.util.containers.ContainerUtil;
18 import org.jetbrains.annotations.NotNull;
19 import org.jetbrains.annotations.Nullable;
20
21 import java.util.Locale;
22 import java.util.Map;
23
24 /**
25  * @author yole
26  */
27 public class StringDescriptorManager {
28   private Module myModule;
29   private final Map<Pair<Locale, String>, PropertiesFile> myPropertiesFileCache = ContainerUtil.createSoftValueMap();
30
31   public StringDescriptorManager(@NotNull Module module) {
32     myModule = module;
33     module.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
34       @Override
35       public void rootsChanged(@NotNull final ModuleRootEvent event) {
36         synchronized(myPropertiesFileCache) {
37           myPropertiesFileCache.clear();
38         }
39       }
40     });
41   }
42
43   public static StringDescriptorManager getInstance(Module module) {
44     StringDescriptorManager service = ModuleServiceManager.getService(module, StringDescriptorManager.class);
45     if (service != null) {
46       service.myModule = module;
47     }
48     return service;
49   }
50
51   @Nullable public String resolve(@NotNull RadComponent component, @Nullable StringDescriptor descriptor) {
52     RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component);
53     Locale locale = (root != null) ? root.getStringDescriptorLocale() : null;
54     return resolve(descriptor, locale);
55   }
56
57   @Nullable public String resolve(@Nullable StringDescriptor descriptor, @Nullable Locale locale) {
58     if (descriptor == null) {
59       return null;
60     }
61
62     if (descriptor.getValue() != null) {
63       return descriptor.getValue();
64     }
65
66     IProperty prop = resolveToProperty(descriptor, locale);
67     if (prop != null) {
68       final String value = prop.getUnescapedValue();
69       if (value != null) {
70         return value;
71       }
72     }
73     // We have to return surrogate string in case if propFile name is invalid or bundle doesn't have specified key
74     return "[" + descriptor.getKey() + " / " + descriptor.getBundleName() + "]";
75   }
76
77   public IProperty resolveToProperty(@NotNull StringDescriptor descriptor, @Nullable Locale locale) {
78     String propFileName = descriptor.getDottedBundleName();
79     Pair<Locale, String> cacheKey = Pair.create(locale, propFileName);
80     PropertiesFile propertiesFile;
81     synchronized (myPropertiesFileCache) {
82       propertiesFile = myPropertiesFileCache.get(cacheKey);
83     }
84     if (propertiesFile == null || !propertiesFile.getContainingFile().isValid()) {
85       propertiesFile = PropertiesUtilBase.getPropertiesFile(propFileName, myModule, locale);
86       synchronized (myPropertiesFileCache) {
87         myPropertiesFileCache.put(cacheKey, propertiesFile);
88       }
89     }
90
91     if (propertiesFile != null) {
92       final IProperty propertyByKey = propertiesFile.findPropertyByKey(descriptor.getKey());
93       if (propertyByKey != null) {
94         return propertyByKey;
95       }
96     }
97     return null;
98   }
99 }