| /* 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.inspections; |
| |
| import com.intellij.codeInspection.InspectionManager; |
| import com.intellij.codeInspection.ProblemDescriptor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.PsiAnnotationMemberValue; |
| import com.intellij.psi.PsiClass; |
| import com.intellij.psi.PsiJavaCodeReferenceElement; |
| import org.jetbrains.annotations.NotNull; |
| import org.qi4j.ide.plugin.idea.common.inspections.AbstractFix; |
| import org.qi4j.ide.plugin.idea.common.inspections.AbstractInspection; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; |
| import static org.qi4j.ide.plugin.idea.common.resource.Qi4jResourceBundle.message; |
| import static org.qi4j.ide.plugin.idea.concerns.common.Qi4jConcernUtil.isAConcern; |
| import static org.qi4j.ide.plugin.idea.mixins.common.Qi4jMixinUtil.*; |
| import static org.qi4j.ide.plugin.idea.sideEffects.common.Qi4jSideEffectUtil.isASideEffect; |
| |
| /** |
| * @author edward.yakop@gmail.com |
| * @since 0.1 |
| */ |
| public final class MixinImplementsMixinType extends AbstractInspection |
| { |
| @NotNull |
| protected final String resourceBundlePrefixId() |
| { |
| return "mixin.implements.mixin.type"; |
| } |
| |
| @NotNull |
| public final String getShortName() |
| { |
| return "MixinImplementsMixinType"; |
| } |
| |
| @Override |
| public final ProblemDescriptor[] checkClass( @NotNull PsiClass psiClass, |
| @NotNull InspectionManager manager, |
| boolean isOnTheFly ) |
| { |
| // If psiClass is not an interface, ignore |
| if( !psiClass.isInterface() ) |
| { |
| return null; |
| } |
| |
| // If @Mixins annotation is empty, ignore |
| List<PsiAnnotationMemberValue> mixinAnnotationValues = getMixinsAnnotationValue( psiClass ); |
| if( mixinAnnotationValues.isEmpty() ) |
| { |
| return null; |
| } |
| |
| // Get all valid mixin type |
| Set<PsiClass> validMixinsType = getAllValidMixinTypes( psiClass ); |
| if( validMixinsType.isEmpty() ) |
| { |
| return null; |
| } |
| |
| // For each mixin |
| List<ProblemDescriptor> problems = new LinkedList<ProblemDescriptor>(); |
| for( PsiAnnotationMemberValue mixinAnnotationValue : mixinAnnotationValues ) |
| { |
| PsiJavaCodeReferenceElement mixinClassReference = getMixinClassReference( mixinAnnotationValue ); |
| |
| // If it's not a class reference, ignore |
| if( mixinClassReference == null ) |
| { |
| continue; |
| } |
| |
| // If class reference can't be resolved, ignore |
| PsiClass mixinClass = (PsiClass) mixinClassReference.resolve(); |
| if( mixinClass == null ) |
| { |
| continue; |
| } |
| |
| String mixinQualifiedName = mixinClass.getQualifiedName(); |
| |
| boolean isMixinsDeclarationValid = false; |
| String message = ""; |
| if( mixinClass.isInterface() ) |
| { |
| // Mixin can't be an interface |
| message = message( "mixin.implements.mixin.type.error.mixin.is.an.interface", mixinQualifiedName ); |
| } |
| else if( isAConcern( mixinClass ) ) |
| { |
| // Mixin can't be a concern |
| message = message( "mixin.implements.mixin.type.error.mixin.is.a.concern", mixinQualifiedName ); |
| } |
| else if( isASideEffect( mixinClass ) ) |
| { |
| // Mixin can't be a side effect |
| message = message( "mixin.implements.mixin.type.error.mixin.is.a.side.effect", mixinQualifiedName ); |
| } |
| else |
| { |
| // If doesn't implement any mixin type, it's a problem |
| if( !isImplementValidMixinType( mixinClass, validMixinsType ) ) |
| { |
| message = message( |
| "mixin.implements.mixin.type.error.does.not.implement.any.mixin.type", |
| mixinQualifiedName, |
| psiClass.getQualifiedName() |
| ); |
| } |
| else |
| { |
| isMixinsDeclarationValid = true; |
| } |
| } |
| |
| if( !isMixinsDeclarationValid ) |
| { |
| ProblemDescriptor problemDescriptor = createProblemDescriptor( |
| manager, mixinAnnotationValue, mixinClassReference, message ); |
| problems.add( problemDescriptor ); |
| } |
| } |
| |
| return problems.toArray( new ProblemDescriptor[problems.size()] ); |
| } |
| |
| private boolean isImplementValidMixinType( PsiClass mixinClass, Set<PsiClass> validMixinsType ) |
| { |
| for( PsiClass validMixinTypeClass : validMixinsType ) |
| { |
| if( mixinClass.isInheritor( validMixinTypeClass, true ) ) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private ProblemDescriptor createProblemDescriptor( @NotNull InspectionManager manager, |
| @NotNull PsiAnnotationMemberValue mixinAnnotationValue, |
| @NotNull PsiJavaCodeReferenceElement mixinClassReference, |
| @NotNull String message ) |
| { |
| RemoveInvalidMixinClassReferenceFix fix = new RemoveInvalidMixinClassReferenceFix( |
| mixinAnnotationValue, mixinClassReference |
| ); |
| return manager.createProblemDescriptor( mixinAnnotationValue, message, fix, GENERIC_ERROR_OR_WARNING ); |
| } |
| |
| private static class RemoveInvalidMixinClassReferenceFix extends AbstractFix |
| { |
| private final PsiAnnotationMemberValue mixinClassAnnotationValue; |
| |
| public RemoveInvalidMixinClassReferenceFix( @NotNull PsiAnnotationMemberValue mixinClassAnnotationValue, |
| @NotNull PsiJavaCodeReferenceElement mixinClassReference ) |
| { |
| super( message( "mixin.implements.mixin.type.fix.remove.class.reference", mixinClassReference.getQualifiedName() ) ); |
| this.mixinClassAnnotationValue = mixinClassAnnotationValue; |
| } |
| |
| public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) |
| { |
| mixinClassAnnotationValue.delete(); |
| } |
| } |
| } |