extensions for getters and setters generation (IDEA-102196)

This commit is contained in:
anna
2013-03-04 22:14:01 +01:00
parent 7de9893f18
commit c0c34005cf
16 changed files with 395 additions and 23 deletions
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiClass;
import com.intellij.util.IncorrectOperationException;
import java.util.ArrayList;
import java.util.Collections;
public class GenerateGetterAndSetterHandler extends GenerateGetterSetterHandlerBase{
private final GenerateGetterHandler myGenerateGetterHandler = new GenerateGetterHandler();
@@ -36,8 +37,8 @@ public class GenerateGetterAndSetterHandler extends GenerateGetterSetterHandlerB
GenerationInfo[] setters = myGenerateSetterHandler.generateMemberPrototypes(aClass, original);
if (getters.length > 0 && setters.length > 0){
array.add(getters[0]);
array.add(setters[0]);
Collections.addAll(array, getters);
Collections.addAll(array, setters);
}
return array.toArray(new GenerationInfo[array.size()]);
@@ -35,7 +35,13 @@ public class GenerateGetterHandler extends GenerateGetterSetterHandlerBase {
@Override
protected GenerationInfo[] generateMemberPrototypes(PsiClass aClass, ClassMember original) throws IncorrectOperationException {
if (original instanceof EncapsulatableClassMember) {
if (original instanceof PropertyClassMember) {
final PropertyClassMember propertyClassMember = (PropertyClassMember)original;
final GenerationInfo[] getters = propertyClassMember.generateGetters();
if (getters != null) {
return getters;
}
} else if (original instanceof EncapsulatableClassMember) {
final EncapsulatableClassMember encapsulatableClassMember = (EncapsulatableClassMember)original;
final GenerationInfo getter = encapsulatableClassMember.generateGetter();
if (getter != null) {
@@ -27,7 +27,14 @@ public class GenerateSetterHandler extends GenerateGetterSetterHandlerBase {
@Override
protected GenerationInfo[] generateMemberPrototypes(PsiClass aClass, ClassMember original) throws IncorrectOperationException {
if (original instanceof EncapsulatableClassMember) {
if (original instanceof PropertyClassMember) {
final PropertyClassMember propertyClassMember = (PropertyClassMember)original;
final GenerationInfo[] getters = propertyClassMember.generateSetters();
if (getters != null) {
return getters;
}
}
else if (original instanceof EncapsulatableClassMember) {
final EncapsulatableClassMember encapsulatableClassMember = (EncapsulatableClassMember)original;
final GenerationInfo setter = encapsulatableClassMember.generateSetter();
if (setter != null) {
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2013 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.codeInsight.generation;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.util.PropertyUtil;
/**
* User: anna
* Date: 3/4/13
*/
public abstract class GetterSetterPrototypeProvider {
public static final ExtensionPointName<GetterSetterPrototypeProvider> EP_NAME = ExtensionPointName.create("com.intellij.getterSetterProvider");
public abstract boolean accept(PsiField field);
public abstract PsiMethod[] generateGetters(PsiField field);
public abstract PsiMethod[] generateSetters(PsiField field);
public abstract boolean isReadOnly(PsiField field);
public static PsiMethod[] generateGetterSetters(PsiField field, boolean generateGetter) {
for (GetterSetterPrototypeProvider provider : Extensions.getExtensions(EP_NAME)) {
if (provider.accept(field)) {
return generateGetter ? provider.generateGetters(field) : provider.generateSetters(field);
}
}
return new PsiMethod[] {generateGetter ? PropertyUtil.generateGetterPrototype(field) : PropertyUtil.generateSetterPrototype(field)};
}
public static boolean isReadOnlyProperty(PsiField field) {
for (GetterSetterPrototypeProvider provider : Extensions.getExtensions(EP_NAME)) {
if (provider.accept(field)) {
return provider.isReadOnly(field);
}
}
return field.hasModifierProperty(PsiModifier.FINAL);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2000-2013 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.codeInsight.generation;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
/**
* User: anna
* Date: 3/4/13
*/
public interface PropertyClassMember extends EncapsulatableClassMember {
/**
* @return PsiElement or TemplateGenerationInfo
*/
@Nullable
GenerationInfo[] generateGetters() throws IncorrectOperationException;
/**
* @return PsiElement or TemplateGenerationInfo
*/
@Nullable
GenerationInfo[] generateSetters() throws IncorrectOperationException;
}
@@ -15,17 +15,23 @@
*/
package com.intellij.codeInsight.generation;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author peter
*/
public class PsiFieldMember extends PsiElementClassMember<PsiField> implements EncapsulatableClassMember {
public class PsiFieldMember extends PsiElementClassMember<PsiField> implements PropertyClassMember {
private static final int FIELD_OPTIONS = PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_TYPE | PsiFormatUtilBase.TYPE_AFTER;
public PsiFieldMember(final PsiField field) {
@@ -36,12 +42,46 @@ public class PsiFieldMember extends PsiElementClassMember<PsiField> implements E
super(psiMember, substitutor, PsiFormatUtil.formatVariable(psiMember, FIELD_OPTIONS, PsiSubstitutor.EMPTY));
}
@Nullable
@Override
public GenerationInfo generateGetter() throws IncorrectOperationException {
final GenerationInfo[] infos = generateGetters();
return infos != null && infos.length > 0 ? infos[0] : null;
}
@Nullable
@Override
public GenerationInfo[] generateGetters() throws IncorrectOperationException {
final PsiField field = getElement();
return createGenerateInfos(field, GetterSetterPrototypeProvider.generateGetterSetters(field, true));
}
@Nullable
@Override
public GenerationInfo generateSetter() throws IncorrectOperationException {
final GenerationInfo[] infos = generateSetters();
return infos != null && infos.length > 0 ? infos[0] : null;
}
@Override
@Nullable
public PsiGenerationInfo generateGetter() {
PsiField field = getElement();
final PsiMethod method = createMethodIfNotExists(field, PropertyUtil.generateGetterPrototype(field));
return method != null ? new PsiGenerationInfo(method) : null;
public GenerationInfo[] generateSetters() {
final PsiField field = getElement();
if (GetterSetterPrototypeProvider.isReadOnlyProperty(field)) {
return null;
}
return createGenerateInfos(field, GetterSetterPrototypeProvider.generateGetterSetters(field, false));
}
private static GenerationInfo[] createGenerateInfos(PsiField field, PsiMethod[] prototypes) {
final List<GenerationInfo> methods = new ArrayList<GenerationInfo>();
for (PsiMethod prototype : prototypes) {
final PsiMethod method = createMethodIfNotExists(field, prototype);
if (method != null) {
methods.add(new PsiGenerationInfo(method));
}
}
return methods.isEmpty() ? null : methods.toArray(new GenerationInfo[methods.size()]);
}
@Nullable
@@ -61,15 +101,4 @@ public class PsiFieldMember extends PsiElementClassMember<PsiField> implements E
return null;
}
}
@Override
@Nullable
public PsiGenerationInfo generateSetter() {
PsiField field = getElement();
if (field.hasModifierProperty(PsiModifier.FINAL)) {
return null;
}
final PsiMethod method = createMethodIfNotExists(field, PropertyUtil.generateSetterPrototype(field));
return method == null ? null : new PsiGenerationInfo(method);
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2013 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 org.jetbrains.plugins.javaFX;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.codeInsight.generation.ClassMember;
import com.intellij.codeInsight.generation.GenerateGetterAndSetterHandler;
import com.intellij.openapi.application.PluginPathManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.testFramework.PsiTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GenerateGetterSetterTest extends DaemonAnalyzerTestCase {
public void testDouble() throws Exception {
doTest();
}
@Override
protected void setUpModule() {
super.setUpModule();
PsiTestUtil.addLibrary(getModule(), "javafx", PluginPathManager.getPluginHomePath("javaFX") + "/testData", "jfxrt.jar");
}
protected void doTest() throws Exception {
configureByFile("/generateGetterSetter/before" + getTestName(false) + ".java");
new GenerateGetterAndSetterHandler() {
@Nullable
@Override
protected ClassMember[] chooseMembers(ClassMember[] members,
boolean allowEmptySelection,
boolean copyJavadocCheckbox,
Project project,
@Nullable Editor editor) {
return members;
}
}.invoke(getProject(), getEditor(), getFile());
checkResultByFile("/generateGetterSetter/after" + getTestName(false) + ".java");
}
@NotNull
@Override
protected String getTestDataPath() {
return PluginPathManager.getPluginHomePath("javaFX") + "/testData";
}
}
@@ -30,6 +30,7 @@
</intentionAction>
<lang.importOptimizer language="XML" implementationClass="org.jetbrains.plugins.javaFX.fxml.codeInsight.JavaFxImportsOptimizer" order="before XML"/>
<psi.referenceContributor implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxReferencesContributor"/>
<getterSetterProvider implementation="org.jetbrains.plugins.javaFX.codeInsight.JavaFxGetterSetterPrototypeProvider"/>
</extensions>
<actions>
@@ -0,0 +1,115 @@
/*
* Copyright 2000-2013 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 org.jetbrains.plugins.javaFX.codeInsight;
import com.intellij.codeInsight.generation.GetterSetterPrototypeProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonClassNames;
import java.util.Map;
/**
* User: anna
* Date: 3/4/13
*/
public class JavaFxGetterSetterPrototypeProvider extends GetterSetterPrototypeProvider {
private static final Logger LOG = Logger.getInstance("#" + JavaFxGetterSetterPrototypeProvider.class.getName());
@Override
public boolean accept(PsiField field) {
return InheritanceUtil.isInheritor(field.getType(), JavaFxCommonClassNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE);
}
@Override
public PsiMethod[] generateGetters(PsiField field) {
final Project project = field.getProject();
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiMethod getter = PropertyUtil.generateGetterPrototype(field);
final PsiType wrappedType = getWrappedType(field, project, JavaFxCommonClassNames.ourReadOnlyMap);
final PsiTypeElement returnTypeElement = getter.getReturnTypeElement();
LOG.assertTrue(returnTypeElement != null);
returnTypeElement.replace(factory.createTypeElement(wrappedType));
final PsiCodeBlock getterBody = getter.getBody();
LOG.assertTrue(getterBody != null);
getterBody.getStatements()[0].replace(factory.createStatementFromText("return " + field.getName() + ".get();", field));
final PsiMethod propertyGetter = PropertyUtil.generateGetterPrototype(field);
propertyGetter.setName(JavaCodeStyleManager.getInstance(project).variableNameToPropertyName(field.getName(), VariableKind.FIELD) + "Property");
return new PsiMethod[] {getter, propertyGetter};
}
@Override
public PsiMethod[] generateSetters(PsiField field) {
final PsiMethod setter = PropertyUtil.generateSetterPrototype(field);
final Project project = field.getProject();
final PsiType wrappedType = getWrappedType(field, project, JavaFxCommonClassNames.ourWritableMap);
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
final PsiTypeElement newTypeElement = elementFactory.createTypeElement(wrappedType);
final PsiParameter[] parameters = setter.getParameterList().getParameters();
LOG.assertTrue(parameters.length == 1);
final PsiParameter parameter = parameters[0];
final PsiTypeElement typeElement = parameter.getTypeElement();
LOG.assertTrue(typeElement != null);
typeElement.replace(newTypeElement);
final PsiCodeBlock body = setter.getBody();
LOG.assertTrue(body != null);
body.getStatements()[0].replace(elementFactory.createStatementFromText("this." + field.getName() + ".set(" + parameter.getName() + ");", field));
return new PsiMethod[] {setter};
}
@Override
public boolean isReadOnly(PsiField field) {
return !InheritanceUtil.isInheritor(field.getType(), JavaFxCommonClassNames.JAVAFX_BEANS_VALUE_WRITABLE_VALUE);
}
private static PsiType getWrappedType(PsiField field, Project project, final Map<String, PsiType> typeMap) {
PsiType substitute = null;
final PsiType fieldType = field.getType();
for (String typeName : typeMap.keySet()) {
if (InheritanceUtil.isInheritor(fieldType, typeName)) {
substitute = typeMap.get(typeName);
break;
}
}
if (substitute == null) {
final PsiClass aClass = JavaPsiFacade.getInstance(project)
.findClass(JavaFxCommonClassNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE, GlobalSearchScope.allScope(project));
LOG.assertTrue(aClass != null);
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(fieldType);
final PsiClass fieldClass = resolveResult.getElement();
LOG.assertTrue(fieldClass != null);
final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(aClass, fieldClass, resolveResult.getSubstitutor());
final PsiMethod[] values = aClass.findMethodsByName("getValue", false);
substitute = substitutor.substitute(values[0].getReturnType());
}
return substitute;
}
}
@@ -15,8 +15,12 @@
*/
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NonNls;
import java.util.HashMap;
import java.util.Map;
/**
* User: anna
* Date: 1/16/13
@@ -35,4 +39,24 @@ public class JavaFxCommonClassNames {
@NonNls public static final String JAVAFX_BEANS_OBSERVABLE = "javafx.beans.Observable";
@NonNls public static final String VALUE_OF = "valueOf";
@NonNls public static final String JAVAFX_FXML_FXMLLOADER = "javafx.fxml.FXMLLoader";
@NonNls public static final String JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE = "javafx.beans.value.ObservableValue";
@NonNls public static final String JAVAFX_BEANS_VALUE_WRITABLE_VALUE = "javafx.beans.value.WritableValue";
public static final Map<String, PsiType> ourWritableMap = new HashMap<String, PsiType>();
static {
ourWritableMap.put("javafx.beans.value.WritableBooleanValue", PsiType.BOOLEAN);
ourWritableMap.put("javafx.beans.value.WritableIntegerValue", PsiType.INT);
ourWritableMap.put("javafx.beans.value.WritableFloatValue", PsiType.FLOAT);
ourWritableMap.put("javafx.beans.value.WritableLongValue", PsiType.LONG);
ourWritableMap.put("javafx.beans.value.WritableDoubleValue", PsiType.DOUBLE);
}
public static final Map<String, PsiType> ourReadOnlyMap = new HashMap<String, PsiType>();
static {
ourReadOnlyMap.put("javafx.beans.property.ReadOnlyBooleanProperty", PsiType.BOOLEAN);
ourReadOnlyMap.put("javafx.beans.property.ReadOnlyIntegerProperty", PsiType.INT);
ourReadOnlyMap.put("javafx.beans.property.ReadOnlyFloatProperty", PsiType.FLOAT);
ourReadOnlyMap.put("javafx.beans.property.ReadOnlyLongProperty", PsiType.LONG);
ourReadOnlyMap.put("javafx.beans.property.ReadOnlyDoubleProperty", PsiType.DOUBLE);
}
}
@@ -140,6 +140,10 @@ public class JavaFxPsiUtil {
}
public static PsiClassType getPropertyClassType(PsiElement field) {
return getPropertyClassType(field, JavaFxCommonClassNames.JAVAFX_BEANS_PROPERTY_OBJECT_PROPERTY);
}
public static PsiClassType getPropertyClassType(PsiElement field, final String superTypeFQN) {
if (field instanceof PsiField) {
final PsiType type = ((PsiField)field).getType();
if (type instanceof PsiClassType) {
@@ -147,7 +151,7 @@ public class JavaFxPsiUtil {
final PsiClass attributeClass = resolveResult.getElement();
if (attributeClass != null) {
final PsiClass objectProperty = JavaPsiFacade.getInstance(attributeClass.getProject())
.findClass(JavaFxCommonClassNames.JAVAFX_BEANS_PROPERTY_OBJECT_PROPERTY, attributeClass.getResolveScope());
.findClass(superTypeFQN, attributeClass.getResolveScope());
if (objectProperty != null) {
final PsiSubstitutor superClassSubstitutor = TypeConversionUtil
.getClassSubstitutor(objectProperty, attributeClass, resolveResult.getSubstitutor());
@@ -0,0 +1,17 @@
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public class Test {
public double getField() {
return field.get();
}
public DoubleProperty fieldProperty() {
return field;
}
public void setField(double field) {
this.field.set(field);
}
private DoubleProperty field = new SimpleDoubleProperty();
}
@@ -0,0 +1,5 @@
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class Test {
private ObjectProperty<String> ob<caret>j = new SimpleObjectProperty<String>();
}
@@ -0,0 +1,5 @@
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public class Test {
private DoubleProperty fie<caret>ld = new SimpleDoubleProperty();
}
@@ -0,0 +1,5 @@
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class Test {
private ObjectProperty<String> ob<caret>j = new SimpleObjectProperty<String>();
}
+2
View File
@@ -169,6 +169,8 @@
<extensionPoint name="codeInsight.createFieldFromUsageHelper" beanClass="com.intellij.lang.LanguageExtensionPoint">
<with attribute="implementationClass" implements="com.intellij.codeInsight.daemon.impl.quickfix.CreateFieldFromUsageHelper"/>
</extensionPoint>
<extensionPoint name="getterSetterProvider" interface="com.intellij.codeInsight.generation.GetterSetterPrototypeProvider"/>
<extensionPoint name="library.dependencyScopeSuggester" interface="com.intellij.openapi.roots.LibraryDependencyScopeSuggester"/>