blob: 2334cec7bbb76a4d301ff9b6353cedfafda5cd0f [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.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();
}
}
}