Extract generic code style property accessor not limited with fields

with example implementation for Java repeat_annotations property
This commit is contained in:
Rustam Vishnyakov
2019-02-06 14:11:15 +03:00
parent d6c22614bf
commit 76ebb5a63b
17 changed files with 186 additions and 103 deletions
@@ -16,7 +16,9 @@
package com.intellij.ide;
import com.intellij.application.options.*;
import com.intellij.application.options.codeStyle.properties.CodeStyleFieldAccessor;
import com.intellij.application.options.codeStyle.properties.CodeStylePropertyAccessor;
import com.intellij.application.options.codeStyle.properties.ValueListPropertyAccessor;
import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationBundle;
@@ -32,6 +34,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import static com.intellij.application.options.JavaDocFormattingPanel.*;
@@ -331,13 +335,51 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett
@Nullable
@Override
public CodeStylePropertyAccessor getAccessor(@NotNull Object codeStyleObject, @NotNull Field field) {
public CodeStyleFieldAccessor getAccessor(@NotNull Object codeStyleObject, @NotNull Field field) {
if (PackageEntryTable.class.isAssignableFrom(field.getType())) {
return new JavaPackageEntryTableAccessor(codeStyleObject, field);
}
return super.getAccessor(codeStyleObject, field);
}
@Override
public List<CodeStylePropertyAccessor> getAdditionalAccessors(@NotNull Object codeStyleObject) {
if (codeStyleObject instanceof JavaCodeStyleSettings) {
return Collections.singletonList(new RepeatAnnotationsAccessor((JavaCodeStyleSettings)codeStyleObject));
}
return super.getAdditionalAccessors(codeStyleObject);
}
private static class RepeatAnnotationsAccessor extends CodeStylePropertyAccessor<List<String>> {
private final JavaCodeStyleSettings mySettings;
RepeatAnnotationsAccessor(@NotNull JavaCodeStyleSettings settings) {
mySettings = settings;
}
@Override
public boolean set(@NotNull List<String> extVal) {
mySettings.setRepeatAnnotations(extVal);
return true;
}
@Override
public List<String> get() {
return mySettings.getRepeatAnnotations();
}
@Override
protected List<String> parseString(@NotNull String string) {
return ValueListPropertyAccessor.getValueList(string);
}
@Override
public String getPropertyName() {
return "repeat_annotations";
}
}
private static final String GENERAL_CODE_SAMPLE =
"public class Foo {\n" +
" public int[] X = new int[]{1, 3, 5, 7, 9, 11};\n" +
@@ -150,6 +150,10 @@
"place_assignment_sign_on_next_line": false,
"prefer_longer_names": true,
"prefer_parameters_wrap": false,
"repeat_annotations": [
"com.jetbrains.First",
"com.jetbrains.Second"
],
"repeat_synchronized": true,
"replace_instanceof_and_cast": false,
"replace_null_check": true,
@@ -140,6 +140,10 @@
"place_assignment_sign_on_next_line": false,
"prefer_longer_names": true,
"prefer_parameters_wrap": false,
"repeat_annotations": [
"com.jetbrains.First",
"com.jetbrains.Second"
],
"repeat_synchronized": true,
"replace_instanceof_and_cast": false,
"replace_null_check": true,
@@ -86,6 +86,7 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase {
final JavaCodeStyleSettings javaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class);
javaSettings.FIELD_NAME_PREFIX = "m_";
javaSettings.STATIC_FIELD_NAME_SUFFIX = "_s";
javaSettings.setRepeatAnnotations(Arrays.asList("com.jetbrains.First", "com.jetbrains.Second"));
CodeStyleSchemeJsonExporter exporter = new CodeStyleSchemeJsonExporter();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -114,6 +115,7 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase {
setSimple(mapper, "doc_align_param_comments", "true");
setList(mapper, "imports_layout",
Arrays.asList("com.jetbrains.*", "blank_line", "org.eclipse.bar", "static **", "static org.eclipse.foo.**"));
mapper.getAccessor("repeat_annotations").setFromString(" com.jetbrains.First, com.jetbrains.Second");
final CommonCodeStyleSettings commonJavaSettings = settings.getCommonSettings(JavaLanguage.INSTANCE);
final JavaCodeStyleSettings javaSettings = settings.getCustomSettings(JavaCodeStyleSettings.class);
assertTrue(commonJavaSettings.ALIGN_GROUP_FIELD_DECLARATIONS);
@@ -127,6 +129,10 @@ public class JavaCodeStyleSettingsTest extends CodeStyleTestCase {
assertEquals(new PackageEntry(false, "org.eclipse.bar", false), importsTable.getEntryAt(2));
assertEquals(PackageEntry.ALL_OTHER_STATIC_IMPORTS_ENTRY, importsTable.getEntryAt(3));
assertEquals(new PackageEntry(true, "org.eclipse.foo", true), importsTable.getEntryAt(4));
List<String> repeatAnno = javaSettings.getRepeatAnnotations();
assertEquals(2, repeatAnno.size());
assertEquals("com.jetbrains.First", repeatAnno.get(0));
assertEquals("com.jetbrains.Second", repeatAnno.get(1));
}
private static void setSimple(@NotNull AbstractCodeStylePropertyMapper mapper, @NotNull String name, @NotNull String value) {
@@ -28,30 +28,21 @@ public abstract class AbstractCodeStylePropertyMapper {
return getAccessorMap().keySet().stream().sorted().collect(Collectors.toList());
}
public List<String> enumPropertiesFor(@NotNull Class... codeStyleClass) {
final Map<String, CodeStylePropertyAccessor> accessorMap = myAccessorMap.getValue();
return accessorMap.keySet().stream().filter(name -> {
CodeStylePropertyAccessor accessor = accessorMap.get(name);
for (Class aClass : codeStyleClass) {
if (accessor.getObjectClass().equals(aClass)) {
return true;
}
}
return false;
}).sorted().collect(Collectors.toList());
}
private Map<String, CodeStylePropertyAccessor> createMap() {
Map<String, CodeStylePropertyAccessor> accessorMap = ContainerUtil.newHashMap();
for (CodeStyleObjectDescriptor descriptor : getSupportedFields()) {
addAccessorsFor(accessorMap, descriptor.getCodeStyleObject(), descriptor.getSupportedFields());
}
addAdditionalAccessors(accessorMap);
return accessorMap;
}
@NotNull
protected abstract List<CodeStyleObjectDescriptor> getSupportedFields();
protected void addAdditionalAccessors(@NotNull Map<String, CodeStylePropertyAccessor> accessorMap) {
}
private void addAccessorsFor(@NotNull Map<String, CodeStylePropertyAccessor> accessorMap,
@NotNull Object codeStyleObject,
@Nullable Set<String> supportedFields) {
@@ -69,7 +60,7 @@ public abstract class AbstractCodeStylePropertyMapper {
@Nullable
protected CodeStylePropertyAccessor getAccessor(@NotNull Object codeStyleObject, @NotNull Field field) {
return new PropertyAccessorFactory(field).createAccessor(codeStyleObject);
return new FieldAccessorFactory(field).createAccessor(codeStyleObject);
}
private List<Field> getCodeStyleFields(Class codeStyleClass) {
@@ -8,7 +8,7 @@ import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
public class BooleanAccessor extends CodeStylePropertyAccessor<Boolean,Boolean> implements CodeStyleChoiceList {
public class BooleanAccessor extends CodeStyleFieldAccessor<Boolean,Boolean> implements CodeStyleChoiceList {
private final static List<String> BOOLEAN_VALS = Arrays.asList("false", "true");
@@ -0,0 +1,81 @@
// 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.
package com.intellij.application.options.codeStyle.properties;
import com.intellij.configurationStore.Property;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
@ApiStatus.Experimental
public abstract class CodeStyleFieldAccessor<T,V> extends CodeStylePropertyAccessor<V> {
private final Object myObject;
private final Field myField;
public CodeStyleFieldAccessor(@NotNull Object object, @NotNull Field field) {
myObject = object;
myField = field;
}
@Override
public boolean set(@NotNull V extVal) {
try {
T value = fromExternal(extVal);
if (value != null) {
myField.set(myObject, value);
return true;
}
}
catch (IllegalAccessException e) {
// Ignore and skip
}
return false;
}
@Override
@Nullable
public V get() {
try {
//noinspection unchecked
T value = (T)myField.get(myObject);
return value != null && !isEmpty(value) ? toExternal(value) : null;
}
catch (IllegalAccessException e) {
// Ignore and return null
}
return null;
}
@NotNull
public Class getObjectClass() {
return myObject.getClass();
}
protected boolean isEmpty(@NotNull T value) {
return false;
}
@Nullable
protected abstract T fromExternal(@NotNull V extVal);
@NotNull
protected abstract V toExternal(@NotNull T value);
@Override
public String getPropertyName() {
Property descriptor = myField.getAnnotation(Property.class);
if (descriptor != null) {
String externalName = descriptor.externalName();
if (!StringUtil.isEmpty(externalName)) return externalName;
}
return PropertyNameUtil.getPropertyName(myField.getName());
}
@Override
public boolean isGenericProperty() {
return myObject instanceof CommonCodeStyleSettings || myObject instanceof CommonCodeStyleSettings.IndentOptions;
}
}
@@ -1,51 +1,12 @@
// Copyright 2000-2018 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.
// 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.
package com.intellij.application.options.codeStyle.properties;
import com.intellij.configurationStore.Property;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
public abstract class CodeStylePropertyAccessor<V> {
public abstract boolean set(@NotNull V extVal);
@ApiStatus.Experimental
public abstract class CodeStylePropertyAccessor<T,V> {
private final Object myObject;
private final Field myField;
public CodeStylePropertyAccessor(@NotNull Object object, @NotNull Field field) {
myObject = object;
myField = field;
}
public boolean set(@NotNull V extVal) {
try {
T value = fromExternal(extVal);
if (value != null) {
myField.set(myObject, value);
return true;
}
}
catch (IllegalAccessException e) {
// Ignore and skip
}
return false;
}
@Nullable
public V get() {
try {
//noinspection unchecked
T value = (T)myField.get(myObject);
return value != null && !isEmpty(value) ? toExternal(value) : null;
}
catch (IllegalAccessException e) {
// Ignore and return null
}
return null;
}
public abstract V get();
public final boolean setFromString(@NotNull String valueString) {
V extValue = parseString(valueString);
@@ -55,34 +16,11 @@ public abstract class CodeStylePropertyAccessor<T,V> {
return false;
}
@Nullable
protected abstract V parseString(@NotNull String string);
@NotNull
public Class getObjectClass() {
return myObject.getClass();
}
protected boolean isEmpty(@NotNull T value) {
public boolean isGenericProperty() {
return false;
}
@Nullable
protected abstract T fromExternal(@NotNull V extVal);
@NotNull
protected abstract V toExternal(@NotNull T value);
public String getPropertyName() {
Property descriptor = myField.getAnnotation(Property.class);
if (descriptor != null) {
String externalName = descriptor.externalName();
if (!StringUtil.isEmpty(externalName)) return externalName;
}
return PropertyNameUtil.getPropertyName(myField.getName());
}
public boolean isGenericProperty() {
return myObject instanceof CommonCodeStyleSettings || myObject instanceof CommonCodeStyleSettings.IndentOptions;
}
public abstract String getPropertyName();
}
@@ -1,7 +1,6 @@
// 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.
package com.intellij.application.options.codeStyle.properties;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
public abstract class ExternalStringAccessor<T> extends CodeStylePropertyAccessor<T,String> {
public abstract class ExternalStringAccessor<T> extends CodeStyleFieldAccessor<T,String> {
public ExternalStringAccessor(@NotNull Object object, @NotNull Field field) {
super(object, field);
@@ -6,11 +6,11 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
class PropertyAccessorFactory {
class FieldAccessorFactory {
private final Field myField;
PropertyAccessorFactory(Field field) {
FieldAccessorFactory(Field field) {
myField = field;
}
@@ -56,7 +56,7 @@ class PropertyAccessorFactory {
}
@Nullable
CodeStylePropertyAccessor createAccessor(@NotNull Object codeStyleObject) {
CodeStyleFieldAccessor createAccessor(@NotNull Object codeStyleObject) {
if (mayHaveAccessor()) {
switch (getValueType()) {
case BOOLEAN:
@@ -95,7 +95,7 @@ class PropertyAccessorFactory {
myField.getAnnotation(Deprecated.class) == null;
}
private static class TabCharPropertyAccessor extends CodeStylePropertyAccessor<Boolean,String> {
private static class TabCharPropertyAccessor extends CodeStyleFieldAccessor<Boolean,String> {
TabCharPropertyAccessor(@NotNull Object object, @NotNull Field field) {
super(object, field);
@@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
public class IntegerAccessor extends CodeStylePropertyAccessor<Integer,Integer> {
public class IntegerAccessor extends CodeStyleFieldAccessor<Integer,Integer> {
IntegerAccessor(@NotNull Object object, @NotNull Field field) {
super(object, field);
}
@@ -22,6 +22,7 @@ public final class LanguageCodeStylePropertyMapper extends AbstractCodeStyleProp
private @NotNull final Language myLanguage;
private @NotNull final String myLanguageDomainId;
private @Nullable final LanguageCodeStyleSettingsProvider mySettingsProvider;
private @NotNull final List<CustomCodeStyleSettings> myCustomSettings;
public LanguageCodeStylePropertyMapper(@NotNull CodeStyleSettings settings,
@NotNull Language language,
@@ -30,6 +31,7 @@ public final class LanguageCodeStylePropertyMapper extends AbstractCodeStyleProp
myLanguage = language;
myLanguageDomainId = languageDomainId == null ? myLanguage.getID().toLowerCase(Locale.ENGLISH) : languageDomainId;
mySettingsProvider = LanguageCodeStyleSettingsProvider.forLanguage(language);
myCustomSettings = getCustomSettings();
}
@Nullable
@@ -42,6 +44,17 @@ public final class LanguageCodeStylePropertyMapper extends AbstractCodeStyleProp
return super.getAccessor(codeStyleObject, field);
}
@Override
protected void addAdditionalAccessors(@NotNull Map<String, CodeStylePropertyAccessor> accessorMap) {
if (mySettingsProvider != null) {
for (CustomCodeStyleSettings customSettings : myCustomSettings) {
for (CodeStylePropertyAccessor accessor : mySettingsProvider.getAdditionalAccessors(customSettings)) {
accessorMap.put(accessor.getPropertyName(), accessor);
}
}
}
}
@NotNull
@Override
protected List<CodeStyleObjectDescriptor> getSupportedFields() {
@@ -51,7 +64,7 @@ public final class LanguageCodeStylePropertyMapper extends AbstractCodeStyleProp
fieldsDescriptors.add(new CodeStyleObjectDescriptor(indentOptions, getSupportedIndentOptions()));
}
fieldsDescriptors.add(new CodeStyleObjectDescriptor(getRootSettings().getCommonSettings(myLanguage), getSupportedLanguageFields()));
for (CustomCodeStyleSettings customSettings : getCustomSettings()) {
for (CustomCodeStyleSettings customSettings : myCustomSettings) {
fieldsDescriptors.add(new CodeStyleObjectDescriptor(customSettings, null));
}
return fieldsDescriptors;
@@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.List;
public abstract class ValueListPropertyAccessor<T> extends CodeStylePropertyAccessor<T, List<String>> {
public abstract class ValueListPropertyAccessor<T> extends CodeStyleFieldAccessor<T, List<String>> {
public ValueListPropertyAccessor(@NotNull Object object, @NotNull Field field) {
super(object, field);
}
@@ -28,7 +28,7 @@ public abstract class ValueListPropertyAccessor<T> extends CodeStylePropertyAcce
}
@NotNull
protected static List<String> getValueList(@NotNull String string) {
public static List<String> getValueList(@NotNull String string) {
return ContainerUtil.map(string.split(","), s -> s.trim());
}
}
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
public class WrapOnTypingAccessor extends CodeStylePropertyAccessor<Integer,Boolean> {
public class WrapOnTypingAccessor extends CodeStyleFieldAccessor<Integer,Boolean> {
WrapOnTypingAccessor(@NotNull Object object, @NotNull Field field) {
super(object, field);
}
@@ -3,6 +3,7 @@ package com.intellij.psi.codeStyle;
import com.intellij.application.options.IndentOptionsEditor;
import com.intellij.application.options.codeStyle.properties.AbstractCodeStylePropertyMapper;
import com.intellij.application.options.codeStyle.properties.CodeStyleFieldAccessor;
import com.intellij.application.options.codeStyle.properties.CodeStylePropertyAccessor;
import com.intellij.application.options.codeStyle.properties.LanguageCodeStylePropertyMapper;
import com.intellij.lang.IdeLanguageCustomization;
@@ -18,10 +19,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -377,7 +375,11 @@ public abstract class LanguageCodeStyleSettingsProvider extends CodeStyleSetting
@ApiStatus.Experimental
@Nullable
public CodeStylePropertyAccessor getAccessor(@NotNull Object codeStyleObject, @NotNull Field field) {
public CodeStyleFieldAccessor getAccessor(@NotNull Object codeStyleObject, @NotNull Field field) {
return null;
}
public List<CodeStylePropertyAccessor> getAdditionalAccessors(@NotNull Object codeStyleObject) {
return Collections.emptyList();
}
}
@@ -67,10 +67,13 @@ public class CodeStyleSchemeJsonExporter extends SchemeExporter<CodeStyleScheme>
else if (externalized instanceof Boolean) {
o.addProperty(name, (Boolean)externalized);
}
else if (externalized != null && accessor instanceof ValueListPropertyAccessor) {
@SuppressWarnings("unchecked") List<String> listValues = (List<String>)externalized;
else if (externalized instanceof List) {
final JsonArray array = new JsonArray();
listValues.forEach(s -> array.add(s));
for (Object element : (List)externalized) {
if (element instanceof String) {
array.add((String)element);
}
}
o.add(name, array);
}
}