blob: be08dde38acccdb2e6736755cb9a052d43f749a3 [file] [log] [blame]
/* Copyright 2008 Edward Yakop.
*
* 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.qi4j.ide.plugin.idea.mixins.common;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
import static com.intellij.codeInsight.AnnotationUtil.findAnnotation;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static org.qi4j.ide.plugin.idea.common.psi.PsiAnnotationUtil.getAnnotationDefaultParameterValue;
import static org.qi4j.ide.plugin.idea.common.psi.PsiAnnotationUtil.getClassReference;
import static org.qi4j.ide.plugin.idea.common.psi.PsiClassUtil.getExtendsDeep;
import static org.qi4j.ide.plugin.idea.common.psi.PsiClassUtil.getPSIClass;
import static org.qi4j.ide.plugin.idea.concerns.common.Qi4jConcernUtil.isAConcern;
import static org.qi4j.ide.plugin.idea.mixins.common.Qi4jMixinConstants.QUALIFIED_NAME_MIXINS;
import static org.qi4j.ide.plugin.idea.sideEffects.common.Qi4jSideEffectUtil.isASideEffect;
/**
* @author edward.yakop@gmail.com
* @since 0.1
*/
public final class Qi4jMixinUtil
{
/**
* Get all valid mixin types of given the {@code psiClass} argument.
*
* @param psiClass The psi class to check.
* @return all vlaid mixin types of the given {@code psiClass} argument.
* @since 0.1
*/
@NotNull
public static Set<PsiClass> getAllValidMixinTypes( @NotNull PsiClass psiClass )
{
PsiAnnotation mixinsAnnotation = getMixinsAnnotation( psiClass );
if( mixinsAnnotation == null )
{
return emptySet();
}
Set<PsiClass> validMixinsType = getExtendsDeep( psiClass );
validMixinsType.add( psiClass );
return validMixinsType;
}
@NotNull
public static List<PsiAnnotationMemberValue> getMixinsAnnotationValue( @NotNull PsiClass psiClass )
{
return getMixinsAnnotationValue( getMixinsAnnotation( psiClass ) );
}
@NotNull
public static List<PsiAnnotationMemberValue> getMixinsAnnotationValue( @Nullable PsiAnnotation mixinsAnnotation )
{
if( mixinsAnnotation == null )
{
return emptyList();
}
String mixinsQualifiedName = mixinsAnnotation.getQualifiedName();
if( !QUALIFIED_NAME_MIXINS.equals( mixinsQualifiedName ) )
{
return emptyList();
}
return getAnnotationDefaultParameterValue( mixinsAnnotation );
}
@Nullable
public static PsiAnnotation getMixinsAnnotation( PsiElement element )
{
PsiClass psiClass = getPSIClass( element );
if( psiClass == null )
{
return null;
}
return findAnnotation( psiClass, QUALIFIED_NAME_MIXINS );
}
@NotNull
public static PsiAnnotation addOrReplaceMixinAnnotation( @NotNull PsiModifierListOwner modifierListOwner,
@NotNull PsiClass mixinClassToAdd )
{
Project project = modifierListOwner.getProject();
JavaPsiFacade psiFacade = JavaPsiFacade.getInstance( project );
PsiElementFactory factory = psiFacade.getElementFactory();
PsiAnnotation existingMixinsAnnotation = findAnnotation( modifierListOwner, QUALIFIED_NAME_MIXINS );
boolean isReplace = false;
PsiAnnotation newMixinsAnnotation;
if( existingMixinsAnnotation != null )
{
// Check duplicate
List<PsiAnnotationMemberValue> mixinsValues = getMixinsAnnotationValue( existingMixinsAnnotation );
for( PsiAnnotationMemberValue mixinValue : mixinsValues )
{
PsiJavaCodeReferenceElement mixinClassReference = getMixinClassReference( mixinValue );
if( mixinClassReference == null )
{
continue;
}
PsiElement mixinClass = mixinClassReference.resolve();
if( mixinClassToAdd.equals( mixinClass ) )
{
return existingMixinsAnnotation;
}
}
isReplace = true;
}
String mixinsAnnotationText = createMixinsAnnotationText( existingMixinsAnnotation, mixinClassToAdd );
newMixinsAnnotation = factory.createAnnotationFromText( mixinsAnnotationText, modifierListOwner );
if( isReplace )
{
// Replace @Mixins instead
existingMixinsAnnotation.replace( newMixinsAnnotation );
}
else
{
// @Mixins doesn't exists, add it as first child
PsiModifierList modifierList = modifierListOwner.getModifierList();
modifierList.addBefore( newMixinsAnnotation, modifierList.getFirstChild() );
}
// Shorten all class references if possible
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance( project );
codeStyleManager.shortenClassReferences( newMixinsAnnotation );
return newMixinsAnnotation;
}
@NotNull
private static String createMixinsAnnotationText( @Nullable PsiAnnotation mixinsAnnotationBase,
@NotNull PsiClass mixinClassToAdd )
{
StringBuilder annotationTextBuilder = new StringBuilder();
annotationTextBuilder.append( "@" ).append( QUALIFIED_NAME_MIXINS ).append( "( {" );
List<PsiAnnotationMemberValue> mixinsValues = getMixinsAnnotationValue( mixinsAnnotationBase );
for( PsiAnnotationMemberValue mixinValue : mixinsValues )
{
annotationTextBuilder.append( mixinValue.getText() ).append( ", " );
}
annotationTextBuilder.append( mixinClassToAdd.getQualifiedName() ).append( ".class" );
annotationTextBuilder.append( "} )" );
return annotationTextBuilder.toString();
}
@Nullable
public static PsiJavaCodeReferenceElement getMixinClassReference( @NotNull PsiAnnotationMemberValue value )
{
return getClassReference( value );
}
/**
* Validate whether psiClass is a mixin.
*
* @param psiClass psi class to check.
* @return {@code true} if psiClass is a mixin, {@code false} otherwise.
*/
public static boolean isAMixin( @NotNull PsiClass psiClass )
{
return !( psiClass.isInterface() || isAConcern( psiClass ) || isASideEffect( psiClass ) );
}
private Qi4jMixinUtil()
{
}
}