blob: 7b9d9fe404f4fb6dc68770ef8230e07e9995306f [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.codeStyle;
import com.intellij.lang.Language;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import org.jdom.Content;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Manages common code style settings for every language using them.
*
* @author Rustam Vishnyakov
*/
public class CommonCodeStyleSettingsManager implements JDOMExternalizable {
private volatile Map<Language, CommonCodeStyleSettings> myCommonSettingsMap;
private volatile Map<String, Content> myUnknownSettingsMap;
@NotNull private final CodeStyleSettings myParentSettings;
@NonNls private static final String COMMON_SETTINGS_TAG = "codeStyleSettings";
private static final String LANGUAGE_ATTR = "language";
CommonCodeStyleSettingsManager(@NotNull CodeStyleSettings parentSettings) {
myParentSettings = parentSettings;
}
/**
* Attempts to get language-specific common settings from <code>LanguageCodeStyleSettingsProvider</code>.
*
* @param lang The language to get settings for.
* @return If the provider for the language exists and is able to create language-specific default settings
* (<code>LanguageCodeStyleSettingsProvider.getDefaultCommonSettings()</code> doesn't return null)
* returns the instance of settings for this language. Otherwise returns the instance of parent settings
* shared between several languages.
*/
public CommonCodeStyleSettings getCommonSettings(@Nullable Language lang) {
Map<Language, CommonCodeStyleSettings> commonSettingsMap = getCommonSettingsMap();
CommonCodeStyleSettings settings = commonSettingsMap.get(lang);
if (settings == null && lang != null) {
settings = commonSettingsMap.get(lang.getBaseLanguage());
}
if (settings != null) {
return settings;
}
return myParentSettings;
}
@NotNull
private Map<Language, CommonCodeStyleSettings> getCommonSettingsMap() {
Map<Language, CommonCodeStyleSettings> commonSettingsMap = myCommonSettingsMap;
if (commonSettingsMap == null) {
synchronized (this) {
commonSettingsMap = myCommonSettingsMap;
if (commonSettingsMap == null) {
commonSettingsMap = initCommonSettingsMap();
initNonReadSettings();
}
}
}
return commonSettingsMap;
}
/**
* Get common code style settings by language name. <code>getCommonSettings(Language)</code> is a preferred method but
* sometimes (for example, in plug-ins which do not depend on a specific language support) language settings can be
* obtained by name.
*
* @param langName The display name of the language whose settings must be returned.
* @return Common code style settings for the given language or parent (shared) settings if not found.
*/
@NotNull
public CommonCodeStyleSettings getCommonSettings(@NotNull String langName) {
Map<Language, CommonCodeStyleSettings> map = getCommonSettingsMap();
for (Map.Entry<Language, CommonCodeStyleSettings> entry : map.entrySet()) {
if (langName.equals(entry.getKey().getDisplayName())) {
return entry.getValue();
}
}
return myParentSettings;
}
private void initNonReadSettings() {
final LanguageCodeStyleSettingsProvider[] providers = Extensions.getExtensions(LanguageCodeStyleSettingsProvider.EP_NAME);
for (final LanguageCodeStyleSettingsProvider provider : providers) {
Language target = provider.getLanguage();
if (!myCommonSettingsMap.containsKey(target)) {
CommonCodeStyleSettings initialSettings = provider.getDefaultCommonSettings();
if (initialSettings != null) {
initialSettings.copyNonDefaultValuesFrom(myParentSettings);
init(initialSettings, target);
}
}
}
}
private void init(@NotNull CommonCodeStyleSettings initialSettings, @NotNull Language target) {
initialSettings.setRootSettings(myParentSettings);
initialSettings.importOldIndentOptions(myParentSettings);
registerCommonSettings(target, initialSettings);
}
private Map<Language, CommonCodeStyleSettings> initCommonSettingsMap() {
Map<Language, CommonCodeStyleSettings> map = new LinkedHashMap<Language, CommonCodeStyleSettings>();
myCommonSettingsMap = map;
myUnknownSettingsMap = new LinkedHashMap<String, Content>();
return map;
}
private void registerCommonSettings(@NotNull Language lang, @NotNull CommonCodeStyleSettings settings) {
synchronized (this) {
if (!myCommonSettingsMap.containsKey(lang)) {
myCommonSettingsMap.put(lang, settings);
settings.getRootSettings(); // check not null
}
}
}
@NotNull
public CommonCodeStyleSettingsManager clone(@NotNull CodeStyleSettings parentSettings) {
synchronized (this) {
CommonCodeStyleSettingsManager settingsManager = new CommonCodeStyleSettingsManager(parentSettings);
if (myCommonSettingsMap != null && !myCommonSettingsMap.isEmpty()) {
settingsManager.initCommonSettingsMap();
for (Map.Entry<Language, CommonCodeStyleSettings> entry : myCommonSettingsMap.entrySet()) {
CommonCodeStyleSettings clonedSettings = entry.getValue().clone(parentSettings);
settingsManager.registerCommonSettings(entry.getKey(), clonedSettings);
}
}
return settingsManager;
}
}
@Override
public void readExternal(@NotNull Element element) throws InvalidDataException {
synchronized (this) {
initCommonSettingsMap();
final List list = element.getChildren(COMMON_SETTINGS_TAG);
for (Object o : list) {
final Element commonSettingsElement = (Element)o;
final String languageId = commonSettingsElement.getAttributeValue(LANGUAGE_ATTR);
if (languageId != null && !languageId.isEmpty()) {
Language target = Language.findLanguageByID(languageId);
boolean isKnownLanguage = target != null;
if (isKnownLanguage) {
final LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(target);
if (provider != null) {
CommonCodeStyleSettings settings = provider.getDefaultCommonSettings();
if (settings != null) {
settings.readExternal(commonSettingsElement);
init(settings, target);
}
}
else {
isKnownLanguage = false;
}
}
if (!isKnownLanguage) {
myUnknownSettingsMap.put(languageId, (Content)commonSettingsElement.clone());
}
}
}
initNonReadSettings();
}
}
@Override
public void writeExternal(@NotNull Element element) throws WriteExternalException {
synchronized (this) {
if (myCommonSettingsMap == null) return;
final Map<String, Language> id2lang = new HashMap<String, Language>();
for (final Language language : myCommonSettingsMap.keySet()) {
id2lang.put(language.getID(), language);
}
final Set<String> langIdList = new HashSet<String>();
langIdList.addAll(myUnknownSettingsMap.keySet());
langIdList.addAll(id2lang.keySet());
final String[] languages = ArrayUtil.toStringArray(langIdList);
Arrays.sort(languages, new Comparator<String>() {
@Override
public int compare(@NotNull final String o1, final String o2) {
return o1.compareTo(o2);
}
});
for (final String id : languages) {
final Language language = id2lang.get(id);
if (language != null) {
final CommonCodeStyleSettings commonSettings = myCommonSettingsMap.get(language);
Element commonSettingsElement = new Element(COMMON_SETTINGS_TAG);
commonSettings.writeExternal(commonSettingsElement);
commonSettingsElement.setAttribute(LANGUAGE_ATTR, language.getID());
if (!commonSettingsElement.getChildren().isEmpty()) {
element.addContent(commonSettingsElement);
}
} else {
final Content unknown = myUnknownSettingsMap.get(id);
if (unknown != null) element.addContent(unknown.detach());
}
}
}
}
public static void copy(@NotNull CommonCodeStyleSettings source, @NotNull CommonCodeStyleSettings target) {
CommonCodeStyleSettings.copyPublicFields(source, target);
CommonCodeStyleSettings.IndentOptions targetIndentOptions = target.getIndentOptions();
if (targetIndentOptions != null) {
CommonCodeStyleSettings.IndentOptions sourceIndentOptions = source.getIndentOptions();
if (sourceIndentOptions != null) {
CommonCodeStyleSettings.copyPublicFields(sourceIndentOptions, targetIndentOptions);
}
}
}
}