| /* 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() |
| { |
| } |
| } |