Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#163 Reference support for @InheritConfiguration and @InheritInverseConfiguration #199

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import static org.mapstruct.intellij.util.MapstructUtil.canDescendIntoType;

/**
* A base reference to target / source annotation.
* A base reference to annotations holding a reference and possibly nested types.
filiphr marked this conversation as resolved.
Show resolved Hide resolved
*
* @author Filip Hrisafov
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.intellij.codeinsight.references;

import java.util.Objects;

import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.intellij.util.MapStructVersion;
import org.mapstruct.intellij.util.MapstructUtil;

import static org.mapstruct.intellij.inspection.inheritance.InheritConfigurationUtils.findInheritConfigurationMethods;

/**
* Reference for {@link InheritConfiguration#name()}.
*
* @author Oliver Erhart
*/
class MapstructMappingInheritConfigurationReference extends MapstructNonNestedBaseReference {

private final MapStructVersion mapStructVersion;

/**
* Create a new {@link MapstructMappingInheritConfigurationReference} with the provided parameters
*
* @param element the element that the reference belongs to
* @param previousReference the previous reference if there is one (in nested properties for example)
* @param rangeInElement the range that the reference represent in the {@code element}
* @param value the matched value (useful when {@code rangeInElement} is empty)
*/
private MapstructMappingInheritConfigurationReference(
PsiElement element,
MapstructMappingInheritConfigurationReference previousReference,
TextRange rangeInElement, String value
) {
super( element, previousReference, rangeInElement, value );
mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile()
.getOriginalFile() );
}

@Override
PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) {

return findInheritConfigurationMethods( mappingMethod, mapStructVersion )
.filter( a -> Objects.equals( a.getName(), value ) )
.findAny()
.orElse( null );
}

@NotNull
@Override
Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) {

return findInheritConfigurationMethods( mappingMethod, mapStructVersion )
.map( method -> MapstructUtil.asLookup( method, method.getName(), method.getName() ) )
.filter( Objects::nonNull )
.toArray();
}

/**
* @param psiElement the literal for which references need to be created
* @return the references for the given {@code psiLiteral}
*/
static PsiReference[] create(PsiElement psiElement) {
return MapstructBaseReference.create( psiElement, MapstructMappingInheritConfigurationReference::new, false );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.intellij.codeinsight.references;

import java.util.Objects;

import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.intellij.util.MapStructVersion;
import org.mapstruct.intellij.util.MapstructUtil;

import static org.mapstruct.intellij.inspection.inheritance.InheritConfigurationUtils.findInheritInverseConfigurationMethods;

/**
* Reference for {@link InheritInverseConfiguration#name()}.
*
* @author Oliver Erhart
*/
class MapstructMappingInheritInverseConfigurationReference extends MapstructNonNestedBaseReference {

private final MapStructVersion mapStructVersion;

/**
* Create a new {@link MapstructMappingInheritInverseConfigurationReference} with the provided parameters
*
* @param element the element that the reference belongs to
* @param previousReference the previous reference if there is one (in nested properties for example)
* @param rangeInElement the range that the reference represent in the {@code element}
* @param value the matched value (useful when {@code rangeInElement} is empty)
*/
private MapstructMappingInheritInverseConfigurationReference(
PsiElement element,
MapstructMappingInheritInverseConfigurationReference previousReference,
TextRange rangeInElement,
String value
) {

super( element, previousReference, rangeInElement, value );
mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile()
.getOriginalFile() );
}

@Override
PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) {

return findInheritInverseConfigurationMethods( mappingMethod, mapStructVersion )
.filter( a -> Objects.equals( a.getName(), value ) )
.findAny()
.orElse( null );
}

@NotNull
@Override
Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) {

return findInheritInverseConfigurationMethods( mappingMethod, mapStructVersion )
.map( method -> MapstructUtil.asLookup( method, method.getName(), method.getName() ) )
.filter( Objects::nonNull )
.toArray();
}

/**
* @param psiElement the literal for which references need to be created
* @return the references for the given {@code psiLiteral}
*/
static PsiReference[] create(PsiElement psiElement) {
return MapstructBaseReference.create(
psiElement,
MapstructMappingInheritInverseConfigurationReference::new,
false
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.intellij.codeInsight.lookup.LookupElement;
Expand All @@ -18,7 +17,6 @@
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
Expand All @@ -31,14 +29,13 @@
import static org.mapstruct.intellij.util.MapstructUtil.NAMED_ANNOTATION_FQN;
import static org.mapstruct.intellij.util.MapstructUtil.MAPPER_ANNOTATION_FQN;
import static org.mapstruct.intellij.util.MapstructUtil.MAPPER_CONFIG_ANNOTATION_FQN;
import static org.mapstruct.intellij.util.MapstructUtil.asLookupWithRepresentableText;

/**
* Reference for {@link org.mapstruct.Mapping#qualifiedByName()}.
*
* @author Oliver Erhart
*/
class MapstructMappingQualifiedByNameReference extends MapstructBaseReference {
class MapstructMappingQualifiedByNameReference extends MapstructNonNestedBaseReference {

/**
* Create a new {@link MapstructMappingQualifiedByNameReference} with the provided parameters
Expand All @@ -54,11 +51,6 @@ private MapstructMappingQualifiedByNameReference(PsiElement element,
super( element, previousReference, rangeInElement, value );
}

@Override
PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) {
return null; // not needed
}

@Override
PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) {

Expand All @@ -80,12 +72,6 @@ private String getNamedValue(PsiMethod method) {
return getStringAttributeValue( namedAnnotation, "value" );
}

@NotNull
@Override
Object[] getVariantsInternal(@NotNull PsiType psiType) {
return LookupElement.EMPTY_ARRAY; // not needed
}

@NotNull
@Override
Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) {
Expand Down Expand Up @@ -139,31 +125,7 @@ private LookupElement methodAsLookup(@NotNull PsiMethod method) {
return null;
}

return asLookupWithRepresentableText(
method,
lookupString,
lookupString,
String.format(
" %s#%s(%s)",
Objects.requireNonNull( method.getContainingClass() ).getName(),
method.getName(),
formatParameters( method )
)
);
}

@NotNull
private static String formatParameters(@NotNull PsiMethod method) {
return Arrays.stream( method.getParameterList().getParameters() )
.map( PsiParameter::getType )
.map( PsiType::getPresentableText )
.collect( Collectors.joining( ", " ) );
}

@Nullable
@Override
PsiType resolvedType() {
return null;
return MapstructUtil.asLookup( method, lookupString, lookupString );
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.intellij.codeinsight.references;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* A base reference to mapstruct annotations without nested types.
*
* @author Oliver Erhart
*/
public abstract class MapstructNonNestedBaseReference extends MapstructBaseReference {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to provide this kind of base reference on the level of MapstructBaseReference but it would have led to more duplication. Therefore I decided to provide a base reference on this level.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that I understand what MapStructNonNestedBaseReference means.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a reference that does not support nesting, see:

This is called with true from source or target references:

Then it is a reference that supports the nested, dotted notation like property.nested.field. So this is possible if the reference is about a type, I guess?

It is false, when nesting is not supported, so in MapstructMappingInheritConfigurationReference, MapstructMappingInheritInverseConfigurationReference, MapstructMappingQualifiedByNameReference where the references are not nested, like method names. And this base class is for those candidates.

Got a better naming idea? I also called it MapstructUntypedBaseReference, but that was even worse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels to me like we need a type like MapStructPropertyReference which would be for the source and target references (which are the only ones where we have nested properties. I wouldn't mix the nested word though in the types, it's a bit strange to me


/**
* Create a reference.
*
* @param element the literal where the text is
* @param previous the previous reference ({@code null} if there is no previous reference)
* @param rangeInElement the range in the {@code element} for which this reference is valid
*/
MapstructNonNestedBaseReference(@NotNull PsiElement element,
@Nullable MapstructBaseReference previous,
TextRange rangeInElement, String value) {
super( element, previous, rangeInElement, value );
}

@Override
final PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) {
return null; // not needed
}

@NotNull
@Override
final Object[] getVariantsInternal(@NotNull PsiType psiType) {
return LookupElement.EMPTY_ARRAY; // not needed
}

@Override
@Nullable
final PsiType resolvedType() {
return null; // not needed
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.jetbrains.annotations.NotNull;

import static org.mapstruct.intellij.util.MapstructElementUtils.beanMappingElementPattern;
import static org.mapstruct.intellij.util.MapstructElementUtils.inheritConfigurationElementPattern;
import static org.mapstruct.intellij.util.MapstructElementUtils.inheritInverseConfigurationElementPattern;
import static org.mapstruct.intellij.util.MapstructElementUtils.mappingElementPattern;
import static org.mapstruct.intellij.util.MapstructElementUtils.valueMappingElementPattern;

Expand Down Expand Up @@ -48,6 +50,16 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar)
valueMappingElementPattern( "target" ),
new MappingTargetReferenceProvider( ValueMappingTargetReference::create )
);

registrar.registerReferenceProvider(
inheritConfigurationElementPattern( "name" ),
new MappingTargetReferenceProvider( MapstructMappingInheritConfigurationReference::create )
);
registrar.registerReferenceProvider(
inheritInverseConfigurationElementPattern( "name" ),
new MappingTargetReferenceProvider( MapstructMappingInheritInverseConfigurationReference::create )
);

}

}
Loading
Loading