Return-Path: X-Original-To: apmail-zest-commits-archive@minotaur.apache.org Delivered-To: apmail-zest-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 997DC186E2 for ; Thu, 30 Jul 2015 19:48:08 +0000 (UTC) Received: (qmail 61556 invoked by uid 500); 30 Jul 2015 19:48:02 -0000 Delivered-To: apmail-zest-commits-archive@zest.apache.org Received: (qmail 61532 invoked by uid 500); 30 Jul 2015 19:48:02 -0000 Mailing-List: contact commits-help@zest.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@zest.apache.org Delivered-To: mailing list commits@zest.apache.org Received: (qmail 60565 invoked by uid 99); 30 Jul 2015 19:48:01 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 30 Jul 2015 19:48:01 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 3B92BE714A; Thu, 30 Jul 2015 19:48:01 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: niclas@apache.org To: commits@zest.apache.org Date: Thu, 30 Jul 2015 19:48:54 -0000 Message-Id: <6c58bcb4004d48e78e280f3501630d37@git.apache.org> In-Reply-To: <4cbd2683fa884aa6bf1c8b2865185ebf@git.apache.org> References: <4cbd2683fa884aa6bf1c8b2865185ebf@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [55/80] zest-java git commit: Stage 2 of the namespace change. Bulk of documentation fixed. http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/inspections/ConcernsAnnotationDeclaredCorrectlyInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/inspections/ConcernsAnnotationDeclaredCorrectlyInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/inspections/ConcernsAnnotationDeclaredCorrectlyInspection.java new file mode 100644 index 0000000..fc598d5 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/inspections/ConcernsAnnotationDeclaredCorrectlyInspection.java @@ -0,0 +1,173 @@ +/* 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.apache.zest.ide.plugin.idea.concerns.inspections; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiAnnotationMemberValue; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractFix; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractInspection; +import org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle; + +import java.util.LinkedList; +import java.util.List; + +import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; +import static org.apache.zest.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope; +import static org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message; +import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernUtil.*; + + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class ConcernsAnnotationDeclaredCorrectlyInspection extends AbstractInspection +{ + @NotNull + protected final String resourceBundlePrefixId() + { + return "concerns.annotation.declared.correctly"; + } + + @NotNull + public final String getShortName() + { + return "ConcernsAnnotationDeclaredCorrectlyInspection"; + } + + @Override + public final ProblemDescriptor[] checkClass( @NotNull PsiClass psiClass, + @NotNull InspectionManager manager, + boolean isOnTheFly ) + { + // If class does not have @Concerns, ignore + PsiAnnotation concernsAnnotation = getConcernsAnnotation( psiClass ); + if( concernsAnnotation == null ) + { + return null; + } + + // If @Concerns declared in class, suggest remove @Concerns annotation + if( !psiClass.isInterface() ) + { + String message = message( "concerns.annotation.declared.correctly.error.annotation.declared.in.class" ); + RemoveConcernsAnnotationFix fix = new RemoveConcernsAnnotationFix( concernsAnnotation ); + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( concernsAnnotation, message, fix, + GENERIC_ERROR_OR_WARNING ); + return new ProblemDescriptor[]{ problemDescriptor }; + } + + // If @Concerns annotation is empty, ignore + List concernsAnnotationValue = getConcernsAnnotationValue( concernsAnnotation ); + if( concernsAnnotationValue.isEmpty() ) + { + return null; + } + + // If ConcernOfClass is not resolved, ignore + Project project = psiClass.getProject(); + GlobalSearchScope searchScope = determineSearchScope( psiClass ); + PsiClass concernOfClass = getConcernOfClass( project, searchScope ); + if( concernOfClass == null ) + { + return null; + } + + List problems = new LinkedList(); + for( PsiAnnotationMemberValue concernClassAnnotationValue : concernsAnnotationValue ) + { + PsiJavaCodeReferenceElement concernClassReference = getConcernClassReference( concernClassAnnotationValue ); + + // If it's not a class reference, ignore + if( concernClassReference == null ) + { + continue; + } + + // If class reference can't be resolved, ignore + PsiClass concernClass = (PsiClass) concernClassReference.resolve(); + if( concernClass == null ) + { + continue; + } + + // If concern class does not inherit concern class, suggest remove that reference. + if( !concernClass.isInheritor( concernOfClass, true ) ) + { + String message = Qi4jResourceBundle.message( + "concerns.annotation.declared.correctly.error.concern.class.does.not.extend.ConcernOf", + concernClass.getQualifiedName() + ); + + RemoveInvalidConcernClassReferenceFix fix = new RemoveInvalidConcernClassReferenceFix( + concernClassAnnotationValue, concernClassReference + ); + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( + concernClassAnnotationValue, message, fix, GENERIC_ERROR_OR_WARNING ); + problems.add( problemDescriptor ); + } + else + { + // TODO: Test whether it is a generic concern + // TODO: Test whether it is a specific concern + } + } + + return problems.toArray( new ProblemDescriptor[problems.size()] ); + } + + private static class RemoveConcernsAnnotationFix extends AbstractFix + { + private final PsiAnnotation annotationToRemove; + + private RemoveConcernsAnnotationFix( @NotNull PsiAnnotation annotationToRemove ) + { + super( message( "concerns.annotation.declared.correctly.fix.remove.annotation" ) ); + this.annotationToRemove = annotationToRemove; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + annotationToRemove.delete(); + } + } + + private static class RemoveInvalidConcernClassReferenceFix extends AbstractFix + { + private final PsiAnnotationMemberValue concernClassAnnotationValue; + + public RemoveInvalidConcernClassReferenceFix( @NotNull PsiAnnotationMemberValue annotationValueToRemove, + @NotNull PsiJavaCodeReferenceElement concernClassReference ) + { + super( message( "concerns.annotation.declared.correctly.fix.remove.concern.class.reference", + concernClassReference.getQualifiedName() ) ); + this.concernClassAnnotationValue = annotationValueToRemove; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + concernClassAnnotationValue.delete(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/intentions/add/AddConcernOnType.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/intentions/add/AddConcernOnType.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/intentions/add/AddConcernOnType.java new file mode 100644 index 0000000..9a06c04 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/intentions/add/AddConcernOnType.java @@ -0,0 +1,138 @@ +/* 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.apache.zest.ide.plugin.idea.concerns.intentions.add; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.Processor; +import com.intellij.util.Query; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.apache.zest.ide.plugin.idea.common.intentions.AbstractIntention; + +import java.util.ArrayList; +import java.util.List; + +import static com.intellij.psi.search.searches.ClassInheritorsSearch.search; +import static java.util.Collections.emptyList; +import static org.apache.zest.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope; +import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernUtil.addOrReplaceConcernAnnotation; +import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernUtil.getConcernOfClass; + +/** + * JAVADOC: This is disabled in Qi4jApplicationComponent. + * + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class AddConcernOnType + extends AbstractIntention +{ + protected boolean isIntentionValidFor( PsiElement element ) + { + if( !( element instanceof PsiClass ) ) + { + return false; + } + + // If it's not interface, ignore it + PsiClass psiClass = (PsiClass) element; + if( !psiClass.isInterface() ) + { + return false; + } + + // Is @Concerns accesible within module + GlobalSearchScope searchScope = determineSearchScope( psiClass ); + PsiClass concernOfClass = getConcernOfClass( psiClass.getProject(), searchScope ); + return concernOfClass != null; + } + + protected final String resourceBundlePrefixId() + { + return "add.concern"; + } + + @Override + public boolean isAvailable( @NotNull Project project, Editor editor, @Nullable PsiElement element ) + { + while( element != null ) + { + if( element instanceof PsiFile || + element instanceof PsiMethod ) + { + break; + } + + if( isIntentionValidFor( element ) ) + { + return true; + } + + element = element.getParent(); + } + + return false; + } + + @SuppressWarnings( "unchecked" ) + protected void processIntention( @NotNull Project project, @NotNull Editor editor, @NotNull PsiElement element ) + { + PsiClass psiClass = (PsiClass) element; + List concernCandidates = findConcernsCandidates( psiClass ); + if( concernCandidates.size() == 1 ) + { + PsiClass concernCandidate = concernCandidates.get( 0 ); + addOrReplaceConcernAnnotation( psiClass, concernCandidate ); + } + } + + private static List findConcernsCandidates( final @NotNull PsiClass classToCheck ) + { + GlobalSearchScope searchScope = determineSearchScope( classToCheck ); + PsiClass concernOfClass = getConcernOfClass( classToCheck.getProject(), searchScope ); + if( concernOfClass == null ) + { + return emptyList(); + } + + Query psiClassQuery = search( concernOfClass, searchScope, true, false ); + final List concernCandidates = new ArrayList(); + psiClassQuery.forEach( new Processor() + { + public boolean process( PsiClass psiClass ) + { + // TODO: Ideally search for all "extends" as well + boolean isInheritor = psiClass.isInheritor( classToCheck, true ); + if( isInheritor ) + { + concernCandidates.add( psiClass ); + } + + return true; + } + } ); + + return concernCandidates; + } +} + http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection.java new file mode 100644 index 0000000..d84fb33 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection.java @@ -0,0 +1,90 @@ +/* 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.apache.zest.ide.plugin.idea.injections.common.inspections; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameter; +import com.intellij.psi.PsiParameterList; +import org.jetbrains.annotations.NotNull; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractFix; + +import java.util.LinkedList; +import java.util.List; + +import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; +import static java.util.Arrays.asList; + +/** + * {@code AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection} is a helper method to check whether + * injection annotation are declared in either constructor or non static field. + * + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public abstract class AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection + extends AbstractInjectionAnnotationDeclarationOnFieldInspection +{ + @Override + public final ProblemDescriptor[] checkMethod( @NotNull PsiMethod method, + @NotNull InspectionManager manager, + boolean isOnTheFly ) + { + PsiParameterList parameterList = method.getParameterList(); + PsiParameter[] parameters = parameterList.getParameters(); + if( method.isConstructor() ) + { + List problems = new LinkedList(); + for( PsiParameter parameter : parameters ) + { + PsiAnnotation annotation = getAnnotationToCheck( parameter ); + if( annotation != null ) + { + ProblemDescriptor[] descriptors = + verifyAnnotationDeclaredCorrectly( parameter, annotation, manager ); + if( descriptors != null ) + { + problems.addAll( asList( descriptors ) ); + } + } + } + + return problems.toArray( new ProblemDescriptor[problems.size()] ); + } + else + { + List problems = new LinkedList(); + for( PsiParameter parameter : parameters ) + { + PsiAnnotation annotationToCheck = getAnnotationToCheck( parameter ); + if( annotationToCheck != null ) + { + String message = getInjectionAnnotationValidDeclarationMessage(); + AbstractFix removeAnnotationFix = createRemoveAnnotationFix( annotationToCheck ); + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( + annotationToCheck, message, removeAnnotationFix, GENERIC_ERROR_OR_WARNING + ); + problems.add( problemDescriptor ); + } + } + + return problems.toArray( new ProblemDescriptor[problems.size()] ); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldInspection.java new file mode 100644 index 0000000..88b3e24 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/common/inspections/AbstractInjectionAnnotationDeclarationOnFieldInspection.java @@ -0,0 +1,138 @@ +/* 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.apache.zest.ide.plugin.idea.injections.common.inspections; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiModifierList; +import com.intellij.psi.PsiVariable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractFix; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractInspection; + +import static com.intellij.codeInsight.AnnotationUtil.findAnnotation; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public abstract class AbstractInjectionAnnotationDeclarationOnFieldInspection extends AbstractInspection +{ + /** + * @return Remove annotation message fix. + * @since 0.1 + */ + @NotNull + protected abstract String getRemoveAnnotationMessageFix(); + + /** + * @return Annotation to check qualified name. + * @since 0.1 + */ + @NotNull + protected abstract String getAnnotationToCheckQualifiedName(); + + /** + * Verified that {@link #getAnnotationToCheck(com.intellij.psi.PsiVariable)} is declared correctly. + * + * @param psiVariable Variable to check. This could be class field member or constructor parameter. + * @param annotationToCheck annotation declared at variable to check. + * @param manager Inspection manager to use to create problem descriptor. + * @return {@code null} if annotation is declared correctly, otherwise an array of problem descriptor. + * @since 0.1 + */ + @Nullable + protected abstract ProblemDescriptor[] verifyAnnotationDeclaredCorrectly( @NotNull PsiVariable psiVariable, + @NotNull PsiAnnotation annotationToCheck, + @NotNull InspectionManager manager ); + + @Override + public final ProblemDescriptor[] checkField( @NotNull PsiField field, + @NotNull InspectionManager manager, + boolean isOnTheFly ) + { + PsiAnnotation annotationToCheck = getAnnotationToCheck( field ); + if( annotationToCheck == null ) + { + return null; + } + + PsiModifierList modifierList = field.getModifierList(); + if( modifierList != null ) + { + if( modifierList.hasModifierProperty( com.intellij.psi.PsiModifier.STATIC ) ) + { + String message = getInjectionAnnotationValidDeclarationMessage(); + AbstractFix removeAnnotationFix = createRemoveAnnotationFix( annotationToCheck ); + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( + annotationToCheck, message, removeAnnotationFix, com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING + ); + + return new ProblemDescriptor[]{ problemDescriptor }; + } + } + + return verifyAnnotationDeclaredCorrectly( field, annotationToCheck, manager ); + } + + /** + * @param variable variable to check. + * @return Annotation to check. + * @see #getAnnotationToCheckQualifiedName() + * @since 0.1 + */ + @Nullable + protected final PsiAnnotation getAnnotationToCheck( @NotNull PsiVariable variable ) + { + String annotationQualifiedName = getAnnotationToCheckQualifiedName(); + return findAnnotation( variable, annotationQualifiedName ); + } + + @NotNull protected String getInjectionAnnotationValidDeclarationMessage() + { + String annotationQualifiedName = getAnnotationToCheckQualifiedName(); + return org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message( "abstract.injection.annotation.declaration.inspection.error.annotation.not.declared.correctly", + annotationQualifiedName ); + } + + @NotNull + protected final AbstractFix createRemoveAnnotationFix( @NotNull PsiAnnotation annotationToRemove ) + { + String fixMessage = getRemoveAnnotationMessageFix(); + return new RemoveAnnotationFix( fixMessage, annotationToRemove ); + } + + private static class RemoveAnnotationFix extends AbstractFix + { + private final PsiAnnotation annotationToRemove; + + public RemoveAnnotationFix( @NotNull String fixMessage, @NotNull PsiAnnotation annotationToRemove ) + { + super( fixMessage ); + this.annotationToRemove = annotationToRemove; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + annotationToRemove.delete(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationConstants.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationConstants.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationConstants.java new file mode 100644 index 0000000..c2004c6 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationConstants.java @@ -0,0 +1,30 @@ +/* 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.apache.zest.ide.plugin.idea.injections.invocation.common; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jInvocationAnnotationConstants +{ + public static final String QUALIFIED_NAME_INVOCATION_ANNOTATION = "org.qi4j.api.injection.scope.Invocation"; + + private Qi4jInvocationAnnotationConstants() + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationUtil.java new file mode 100644 index 0000000..c7ef6da --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/common/Qi4jInvocationAnnotationUtil.java @@ -0,0 +1,127 @@ +/* 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.apache.zest.ide.plugin.idea.injections.invocation.common; + +import com.intellij.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.codeInsight.AnnotationUtil.findAnnotation; +import static com.intellij.psi.PsiModifier.STATIC; +import static org.apache.zest.ide.plugin.idea.common.psi.PsiClassUtil.getPSIClass; +import static org.apache.zest.ide.plugin.idea.injections.invocation.common.Qi4jInvocationAnnotationConstants.QUALIFIED_NAME_INVOCATION_ANNOTATION; +import static org.apache.zest.ide.plugin.idea.injections.invocation.common.Qi4jInvocationAnnotationUtil.InvocationAnnotationDeclarationValidationResult.*; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.isInjecteableByStructureAnnotation; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jInvocationAnnotationUtil +{ + /** + * Returns {@code @Invocation} annotation if exists. + * + * @param modifierListOwner modifier list owner to process. + * @return {@code @Invocation} annotation if exists, {@code null} otherwise. + * @since 0.1 + */ + @Nullable + public static PsiAnnotation getInvocationAnnotation( @NotNull PsiModifierListOwner modifierListOwner ) + { + return findAnnotation( modifierListOwner, QUALIFIED_NAME_INVOCATION_ANNOTATION ); + } + + /** + * @param psiClass psi class to check. + * @return {@code true} if the specified psiClass is injectable by invocation annotation, {@code false} otherwise. + */ + public static boolean isInjectableByInvocationAnnotation( @NotNull PsiClass psiClass ) + { + if( psiClass.isAnnotationType() ) + { + return true; + } + + String classQualifiedName = psiClass.getQualifiedName(); + return "java.lang.reflect.Method".equals( classQualifiedName ) || + "java.lang.reflect.AnnotatedElement".equals( classQualifiedName ); + } + + /** + * Validates whether the variable has {@code @Invocation} annotation declared correctly. + * + * @param variable variable to check. + * @return Look at {@link InvocationAnnotationDeclarationValidationResult}. + * @since 0.1 + */ + @NotNull + public static InvocationAnnotationDeclarationValidationResult isValidInvocationAnnotationDeclaration( + @NotNull PsiVariable variable ) + { + PsiAnnotation invocationAnnotation = getInvocationAnnotation( variable ); + if( invocationAnnotation == null ) + { + return invalidInvocationAnnotationNotDeclared; + } + + PsiModifierList modifierList = variable.getModifierList(); + if( modifierList != null ) + { + if( modifierList.hasModifierProperty( STATIC ) ) + { + return invalidDeclaredOnStaticVariable; + } + } + + // TODO: Check whether variable is either an instance of java.lang.reflect.Method or + // java.lang.reflect.AnnotatedElement or Annotation + PsiTypeElement typeElement = variable.getTypeElement(); + if( typeElement != null ) + { + PsiClass psiClass = getPSIClass( typeElement ); + if( psiClass != null ) + { + if( !isInjectableByInvocationAnnotation( psiClass ) ) + { + // Can't be type that is injected by @Structure + if( isInjecteableByStructureAnnotation( variable ) ) + { + return invalidTypeIsInjectedViaStructureAnnotation; + } + + return invalidType; + } + } + } + + return valid; + } + + public enum InvocationAnnotationDeclarationValidationResult + { + invalidInvocationAnnotationNotDeclared, + invalidDeclaredOnStaticVariable, + invalidTypeIsInjectedViaStructureAnnotation, + invalidType, + valid, + } + + private Qi4jInvocationAnnotationUtil() + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/inspections/InvocationAnnotationDeclaredCorrectlyInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/inspections/InvocationAnnotationDeclaredCorrectlyInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/inspections/InvocationAnnotationDeclaredCorrectlyInspection.java new file mode 100644 index 0000000..57e9b60 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/invocation/inspections/InvocationAnnotationDeclaredCorrectlyInspection.java @@ -0,0 +1,119 @@ +/* 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.apache.zest.ide.plugin.idea.injections.invocation.inspections; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiVariable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.apache.zest.ide.plugin.idea.injections.common.inspections.AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection; +import org.apache.zest.ide.plugin.idea.injections.structure.common.ReplaceWithStructureAnnotation; + +import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; +import static org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message; +import static org.apache.zest.ide.plugin.idea.injections.invocation.common.Qi4jInvocationAnnotationConstants.QUALIFIED_NAME_INVOCATION_ANNOTATION; +import static org.apache.zest.ide.plugin.idea.injections.invocation.common.Qi4jInvocationAnnotationUtil.InvocationAnnotationDeclarationValidationResult; +import static org.apache.zest.ide.plugin.idea.injections.invocation.common.Qi4jInvocationAnnotationUtil.isValidInvocationAnnotationDeclaration; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.getStructureAnnotation; + +/** + * {@code InvocationAnnotationDeclaredCorrectlyInspection} validates {@code @Invocation} injection annotation + * declaration. + * + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public class InvocationAnnotationDeclaredCorrectlyInspection + extends AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection +{ + @NotNull + protected final String resourceBundlePrefixId() + { + return "injections.invocation.annotation.declared.correctly"; + } + + @NotNull + public final String getShortName() + { + return "InvocationAnnotationDeclaredCorrectlyInspection"; + } + + @NotNull + protected final String getRemoveAnnotationMessageFix() + { + return message( "injections.invocation.annotation.declared.correctly.fix.remove.annotation" ); + } + + @NotNull + protected final String getAnnotationToCheckQualifiedName() + { + return QUALIFIED_NAME_INVOCATION_ANNOTATION; + } + + @Nullable + protected final ProblemDescriptor[] verifyAnnotationDeclaredCorrectly( @NotNull PsiVariable psiVariable, + @NotNull PsiAnnotation invocationAnnotation, + @NotNull InspectionManager manager ) + { + LocalQuickFix fix = null; + String message = null; + + String variableTypeQualifiedName = psiVariable.getType().getCanonicalText(); + + InvocationAnnotationDeclarationValidationResult validationResult = + isValidInvocationAnnotationDeclaration( psiVariable ); + switch( validationResult ) + { + case invalidTypeIsInjectedViaStructureAnnotation: + if( getStructureAnnotation( psiVariable ) == null ) + { + fix = new ReplaceWithStructureAnnotation( + message( "injections.invocation.annotation.declared.correctly.fix.replace.with.structure.annotation" ), + invocationAnnotation ); + } + message = message( + "injections.invocation.annotation.declared.correctly.error.type.is.injected.by.structure", + variableTypeQualifiedName + ); + break; + + case invalidType: + message = message( "injections.invocation.annotation.declared.correctly.error.type.is.not.injectable", + variableTypeQualifiedName ); + break; + } + + // If it's not an error, return null + if( message == null ) + { + return null; + } + + // If Fix not defined, by default we remove it. + if( fix == null ) + { + fix = createRemoveAnnotationFix( invocationAnnotation ); + } + + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( + invocationAnnotation, message, fix, GENERIC_ERROR_OR_WARNING ); + return new ProblemDescriptor[]{ problemDescriptor }; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationConstants.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationConstants.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationConstants.java new file mode 100644 index 0000000..4ab8602 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationConstants.java @@ -0,0 +1,30 @@ +/* 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.apache.zest.ide.plugin.idea.injections.service.common; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jServiceAnnotationConstants +{ + public static final String QUALIFIED_NAME_SERVICE_ANNOTATION = "org.qi4j.api.injection.scope.Service"; + + private Qi4jServiceAnnotationConstants() + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationUtil.java new file mode 100644 index 0000000..824466c --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/common/Qi4jServiceAnnotationUtil.java @@ -0,0 +1,97 @@ +/* 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.apache.zest.ide.plugin.idea.injections.service.common; + +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiModifierList; +import com.intellij.psi.PsiModifierListOwner; +import com.intellij.psi.PsiVariable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.codeInsight.AnnotationUtil.findAnnotation; +import static com.intellij.psi.PsiModifier.STATIC; +import static org.apache.zest.ide.plugin.idea.injections.service.common.Qi4jServiceAnnotationConstants.QUALIFIED_NAME_SERVICE_ANNOTATION; +import static org.apache.zest.ide.plugin.idea.injections.service.common.Qi4jServiceAnnotationUtil.ServiceAnnotationDeclarationValidationResult.*; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.isInjecteableByStructureAnnotation; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jServiceAnnotationUtil +{ + /** + * Returns {@code @Service} annotation if exists. + * + * @param modifierListOwner modifier list owner to process. + * @return {@code @Service} annotation if exists, {@code null} otherwise. + * @since 0.1 + */ + @Nullable + public static PsiAnnotation getServiceAnnotation( @NotNull PsiModifierListOwner modifierListOwner ) + { + return findAnnotation( modifierListOwner, QUALIFIED_NAME_SERVICE_ANNOTATION ); + } + + /** + * Validates whether the variable has {@code @Service} annotation declared correctly. + * + * @param variable variable to check. + * @return Look at {@link ServiceAnnotationDeclarationValidationResult}. + * @since 0.1 + */ + @NotNull + public static ServiceAnnotationDeclarationValidationResult isValidServiceAnnotationDeclaration( + @NotNull PsiVariable variable ) + { + PsiAnnotation serviceAnnotation = getServiceAnnotation( variable ); + if( serviceAnnotation == null ) + { + return invalidServiceAnnotationNotDeclared; + } + + PsiModifierList modifierList = variable.getModifierList(); + if( modifierList != null ) + { + if( modifierList.hasModifierProperty( STATIC ) ) + { + return invalidDeclaredOnStaticVariable; + } + } + + // Can't be type that is injected by @Structure + if( isInjecteableByStructureAnnotation( variable ) ) + { + return invalidTypeIsInjectedViaStructureAnnotation; + } + + return valid; + } + + public enum ServiceAnnotationDeclarationValidationResult + { + invalidServiceAnnotationNotDeclared, + invalidDeclaredOnStaticVariable, + invalidTypeIsInjectedViaStructureAnnotation, + valid, + } + + private Qi4jServiceAnnotationUtil() + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/inspections/ServiceAnnotationDeclaredCorrectlyInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/inspections/ServiceAnnotationDeclaredCorrectlyInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/inspections/ServiceAnnotationDeclaredCorrectlyInspection.java new file mode 100644 index 0000000..be3a82b --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/service/inspections/ServiceAnnotationDeclaredCorrectlyInspection.java @@ -0,0 +1,110 @@ +/* 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.apache.zest.ide.plugin.idea.injections.service.inspections; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiVariable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.apache.zest.ide.plugin.idea.injections.common.inspections.AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection; +import org.apache.zest.ide.plugin.idea.injections.structure.common.ReplaceWithStructureAnnotation; + +import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; +import static org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message; +import static org.apache.zest.ide.plugin.idea.injections.service.common.Qi4jServiceAnnotationConstants.QUALIFIED_NAME_SERVICE_ANNOTATION; +import static org.apache.zest.ide.plugin.idea.injections.service.common.Qi4jServiceAnnotationUtil.ServiceAnnotationDeclarationValidationResult; +import static org.apache.zest.ide.plugin.idea.injections.service.common.Qi4jServiceAnnotationUtil.isValidServiceAnnotationDeclaration; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.getStructureAnnotation; + +/** + * {@code ServiceAnnotationDeclaredCorrectly} validates {@code @Service} injection annotation declaration. + * + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public class ServiceAnnotationDeclaredCorrectlyInspection + extends AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection +{ + @NotNull + protected final String resourceBundlePrefixId() + { + return "injections.service.annotation.declared.correctly"; + } + + @NotNull + public final String getShortName() + { + return "ServiceAnnotationDeclaredCorrectlyInspection"; + } + + @NotNull + protected final String getRemoveAnnotationMessageFix() + { + return message( "injections.service.annotation.declared.correctly.fix.remove.annotation" ); + } + + @NotNull + protected final String getAnnotationToCheckQualifiedName() + { + return QUALIFIED_NAME_SERVICE_ANNOTATION; + } + + @Nullable + protected final ProblemDescriptor[] verifyAnnotationDeclaredCorrectly( @NotNull PsiVariable psiVariable, + @NotNull PsiAnnotation serviceAnnotation, + @NotNull InspectionManager manager ) + { + ServiceAnnotationDeclarationValidationResult annotationCheck = + isValidServiceAnnotationDeclaration( psiVariable ); + String message = null; + LocalQuickFix fix = null; + switch( annotationCheck ) + { + case invalidTypeIsInjectedViaStructureAnnotation: + if( getStructureAnnotation( psiVariable ) == null ) + { + fix = new ReplaceWithStructureAnnotation( + message( "injections.service.annotation.declared.correctly.fix.replace.with.structure.annotation" ), + serviceAnnotation ); + } + message = message( + "injections.service.annotation.declared.correctly.error.type.is.injected.by.structure", + psiVariable.getType().getCanonicalText() + ); + break; + } + + // If it's not an error, return null + if( message == null ) + { + return null; + } + + // Default behavior to remove @Service annotation + if( fix == null ) + { + fix = createRemoveAnnotationFix( serviceAnnotation ); + } + + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( + serviceAnnotation, message, fix, GENERIC_ERROR_OR_WARNING ); + return new ProblemDescriptor[]{ problemDescriptor }; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationConstants.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationConstants.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationConstants.java new file mode 100644 index 0000000..9635ad4 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationConstants.java @@ -0,0 +1,51 @@ +/* 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.apache.zest.ide.plugin.idea.injections.structure.common; + +import static java.util.Arrays.sort; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jStructureAnnotationConstants +{ + public static final String QUALIFIED_NAME_STRUCTURE_ANNOTATION = "org.qi4j.api.injection.scope.Structure"; + + public static final String[] VALID_STRUCTURE_INJECTION_TYPE; + + static + { + VALID_STRUCTURE_INJECTION_TYPE = new String[] + { + "org.qi4j.composite.CompositeBuilderFactory", + "org.qi4j.object.ObjectBuilderFactory", + "org.qi4j.entity.UnitOfWorkFactory", + "org.qi4j.service.ServiceFinder", + "org.qi4j.structure.Module", + "org.qi4j.structure.Layer", + "org.qi4j.structure.Application", + "org.qi4j.Qi4j", + "org.qi4j.spi.Qi4jSPI" + }; + sort( VALID_STRUCTURE_INJECTION_TYPE ); + } + + private Qi4jStructureAnnotationConstants() + { + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationUtil.java new file mode 100644 index 0000000..da54dd9 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/Qi4jStructureAnnotationUtil.java @@ -0,0 +1,122 @@ +/* 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.apache.zest.ide.plugin.idea.injections.structure.common; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.codeInsight.AnnotationUtil.findAnnotation; +import static com.intellij.psi.PsiModifier.STATIC; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationConstants.QUALIFIED_NAME_STRUCTURE_ANNOTATION; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationConstants.VALID_STRUCTURE_INJECTION_TYPE; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.StructureAnnotationDeclarationValidationResult.*; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jStructureAnnotationUtil +{ + /** + * Returns {@code Structure} annotation if exists. + * + * @param modifierListOwner Modifier list owner. + * @return @Structure annotation if exists, {@code null} otherwise. + * @since 0.1 + */ + @Nullable + public static PsiAnnotation getStructureAnnotation( @NotNull PsiModifierListOwner modifierListOwner ) + { + return findAnnotation( modifierListOwner, QUALIFIED_NAME_STRUCTURE_ANNOTATION ); + } + + /** + * Create structure annotation. + * + * @param project project to create structure annotation. + * @param context the context to create structure annotation. + * @return @Structure annotation. + */ + @NotNull + public static PsiAnnotation createStructureAnnotation( @NotNull Project project, + @NotNull PsiElement context ) + { + JavaPsiFacade psiFacade = JavaPsiFacade.getInstance( project ); + PsiElementFactory factory = psiFacade.getElementFactory(); + return factory.createAnnotationFromText( "@" + QUALIFIED_NAME_STRUCTURE_ANNOTATION, context ); + } + + /** + * @param variable variable to check. + * @return Look at {@link StructureAnnotationDeclarationValidationResult}. + * @since 0.1 + */ + @NotNull + public static StructureAnnotationDeclarationValidationResult validateStructureAnnotationDeclaration( + @NotNull PsiVariable variable ) + { + PsiAnnotation structureAnnotation = getStructureAnnotation( variable ); + if( structureAnnotation == null ) + { + return invalidStructureAnnotationNotDeclared; + } + + PsiModifierList modifierList = variable.getModifierList(); + if( modifierList != null ) + { + if( modifierList.hasModifierProperty( STATIC ) ) + { + return invalidDeclaredOnStaticVariable; + } + } + + if( !isInjecteableByStructureAnnotation( variable ) ) + { + return invalidInjectionType; + } + + return valid; + } + + /** + * Returns a {@code boolean} indicator whether variable type is injectable by @Structure annotation. + * + * @param variable variable to check. + * @return {@code true} if variable type is injecteable by @Structure annotation. + * @since 0.1 + */ + public static boolean isInjecteableByStructureAnnotation( @NotNull PsiVariable variable ) + { + PsiType type = variable.getType(); + String fieldClassQualifiedName = type.getCanonicalText(); + return binarySearch( VALID_STRUCTURE_INJECTION_TYPE, fieldClassQualifiedName ) > -1; + } + + private Qi4jStructureAnnotationUtil() + { + } + + public enum StructureAnnotationDeclarationValidationResult + { + invalidStructureAnnotationNotDeclared, + invalidDeclaredOnStaticVariable, + invalidInjectionType, + valid, + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/ReplaceWithStructureAnnotation.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/ReplaceWithStructureAnnotation.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/ReplaceWithStructureAnnotation.java new file mode 100644 index 0000000..3cd6fb6 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/common/ReplaceWithStructureAnnotation.java @@ -0,0 +1,47 @@ +/* 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.apache.zest.ide.plugin.idea.injections.structure.common; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiAnnotation; +import org.jetbrains.annotations.NotNull; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractFix; + +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.createStructureAnnotation; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class ReplaceWithStructureAnnotation extends AbstractFix +{ + private final PsiAnnotation annotation; + + public ReplaceWithStructureAnnotation( @NotNull String fixMessage, + @NotNull PsiAnnotation annotationToReplace ) + { + super( fixMessage ); + this.annotation = annotationToReplace; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + PsiAnnotation structureAnnotation = createStructureAnnotation( project, annotation ); + annotation.replace( structureAnnotation ); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/inspections/StructureAnnotationDeclaredCorrectlyInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/inspections/StructureAnnotationDeclaredCorrectlyInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/inspections/StructureAnnotationDeclaredCorrectlyInspection.java new file mode 100644 index 0000000..7baea38 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/injections/structure/inspections/StructureAnnotationDeclaredCorrectlyInspection.java @@ -0,0 +1,90 @@ +/* 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.apache.zest.ide.plugin.idea.injections.structure.inspections; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiVariable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractFix; +import org.apache.zest.ide.plugin.idea.injections.common.inspections.AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection; + +import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; +import static org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationConstants.QUALIFIED_NAME_STRUCTURE_ANNOTATION; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.StructureAnnotationDeclarationValidationResult; +import static org.apache.zest.ide.plugin.idea.injections.structure.common.Qi4jStructureAnnotationUtil.validateStructureAnnotationDeclaration; + +/** + * {@code StructureAnnotationUsedCorrectly} validates {@code @Structure} injection annotation declaration. + * + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public class StructureAnnotationDeclaredCorrectlyInspection + extends AbstractInjectionAnnotationDeclarationOnFieldAndConstructorInspection +{ + @NotNull + protected final String resourceBundlePrefixId() + { + return "injections.structure.annotation.declared.correctly"; + } + + @NotNull + public final String getShortName() + { + return "StructureAnnotationDeclaredCorrectlyInspection"; + } + + @NotNull + protected final String getRemoveAnnotationMessageFix() + { + return message( "injections.structure.annotation.declared.correctly.fix.remove.annotation" ); + } + + @NotNull + protected final String getAnnotationToCheckQualifiedName() + { + return QUALIFIED_NAME_STRUCTURE_ANNOTATION; + } + + @Nullable + protected final ProblemDescriptor[] verifyAnnotationDeclaredCorrectly( @NotNull PsiVariable psiVariable, + @NotNull PsiAnnotation structureAnnotation, + @NotNull InspectionManager manager ) + { + StructureAnnotationDeclarationValidationResult annotationCheck = + validateStructureAnnotationDeclaration( psiVariable ); + switch( annotationCheck ) + { + case invalidInjectionType: + String message = message( + "injections.structure.annotation.declared.correctly.error.invalid.injection.type", + psiVariable.getType().getCanonicalText() + ); + AbstractFix removeStructureAnnotationFix = createRemoveAnnotationFix( structureAnnotation ); + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( + structureAnnotation, message, removeStructureAnnotationFix, GENERIC_ERROR_OR_WARNING + ); + return new ProblemDescriptor[]{ problemDescriptor }; + } + + return null; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinConstants.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinConstants.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinConstants.java new file mode 100644 index 0000000..e2f02df --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinConstants.java @@ -0,0 +1,30 @@ +/* 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.apache.zest.ide.plugin.idea.mixins.common; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jMixinConstants +{ + public static final String QUALIFIED_NAME_MIXINS = "org.qi4j.api.mixin.Mixins"; + + private Qi4jMixinConstants() + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinUtil.java new file mode 100644 index 0000000..e8100bd --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/common/Qi4jMixinUtil.java @@ -0,0 +1,194 @@ +/* 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.apache.zest.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.apache.zest.ide.plugin.idea.common.psi.PsiAnnotationUtil.getAnnotationDefaultParameterValue; +import static org.apache.zest.ide.plugin.idea.common.psi.PsiAnnotationUtil.getClassReference; +import static org.apache.zest.ide.plugin.idea.common.psi.PsiClassUtil.getExtendsDeep; +import static org.apache.zest.ide.plugin.idea.common.psi.PsiClassUtil.getPSIClass; +import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernUtil.isAConcern; +import static org.apache.zest.ide.plugin.idea.mixins.common.Qi4jMixinConstants.QUALIFIED_NAME_MIXINS; +import static org.apache.zest.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 getAllValidMixinTypes( @NotNull PsiClass psiClass ) + { + PsiAnnotation mixinsAnnotation = getMixinsAnnotation( psiClass ); + if( mixinsAnnotation == null ) + { + return emptySet(); + } + + Set validMixinsType = getExtendsDeep( psiClass ); + validMixinsType.add( psiClass ); + return validMixinsType; + } + + @NotNull + public static List getMixinsAnnotationValue( @NotNull PsiClass psiClass ) + { + return getMixinsAnnotationValue( getMixinsAnnotation( psiClass ) ); + } + + @NotNull + public static List 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 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 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() + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinImplementsMixinType.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinImplementsMixinType.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinImplementsMixinType.java new file mode 100644 index 0000000..f03722f --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinImplementsMixinType.java @@ -0,0 +1,188 @@ +/* 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.apache.zest.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.apache.zest.ide.plugin.idea.common.inspections.AbstractFix; +import org.apache.zest.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.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message; +import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernUtil.isAConcern; +import static org.apache.zest.ide.plugin.idea.mixins.common.Qi4jMixinUtil.*; +import static org.apache.zest.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 mixinAnnotationValues = getMixinsAnnotationValue( psiClass ); + if( mixinAnnotationValues.isEmpty() ) + { + return null; + } + + // Get all valid mixin type + Set validMixinsType = getAllValidMixinTypes( psiClass ); + if( validMixinsType.isEmpty() ) + { + return null; + } + + // For each mixin + List problems = new LinkedList(); + 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 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(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinsAnnotationDeclaredOnMixinType.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinsAnnotationDeclaredOnMixinType.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinsAnnotationDeclaredOnMixinType.java new file mode 100644 index 0000000..c4ebfde --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/mixins/inspections/MixinsAnnotationDeclaredOnMixinType.java @@ -0,0 +1,90 @@ +/* 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.apache.zest.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.PsiAnnotation; +import com.intellij.psi.PsiAnnotationMemberValue; +import com.intellij.psi.PsiClass; +import org.jetbrains.annotations.NotNull; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractFix; +import org.apache.zest.ide.plugin.idea.common.inspections.AbstractInspection; + +import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; +import static org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message; +import static org.apache.zest.ide.plugin.idea.mixins.common.Qi4jMixinUtil.getMixinsAnnotation; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class MixinsAnnotationDeclaredOnMixinType extends AbstractInspection +{ + @NotNull + public final String getShortName() + { + return "MixinsAnnotationDeclaredOnMixinType"; + } + + @NotNull + protected final String resourceBundlePrefixId() + { + return "mixins.annotation.declared.on.mixin.type"; + } + + @Override + public ProblemDescriptor[] checkClass( @NotNull PsiClass psiClass, + @NotNull InspectionManager manager, + boolean isOnTheFly ) + { + PsiAnnotation mixinsAnnotation = getMixinsAnnotation( psiClass ); + if( mixinsAnnotation == null ) + { + return null; + } + + if( psiClass.isInterface() ) + { + return null; + } + + String message = message( "mixins.annotation.declared.on.mixin.type.error.declared.on.class" ); + RemoveInvalidMixinClassReferenceFix fix = new RemoveInvalidMixinClassReferenceFix( mixinsAnnotation ); + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( mixinsAnnotation, message, fix, + GENERIC_ERROR_OR_WARNING ); + return new ProblemDescriptor[]{ problemDescriptor }; + + } + + private static class RemoveInvalidMixinClassReferenceFix extends AbstractFix + { + private final PsiAnnotationMemberValue mixinsAnnotation; + + public RemoveInvalidMixinClassReferenceFix( @NotNull PsiAnnotationMemberValue mixinsAnnotation ) + { + super( message( "mixins.annotation.declared.on.mixin.type.fix.remove.mixins.annotation" ) ); + this.mixinsAnnotation = mixinsAnnotation; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + mixinsAnnotation.delete(); + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/sideEffects/common/Qi4jSideEffectConstants.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/sideEffects/common/Qi4jSideEffectConstants.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/sideEffects/common/Qi4jSideEffectConstants.java new file mode 100644 index 0000000..d417575 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/sideEffects/common/Qi4jSideEffectConstants.java @@ -0,0 +1,33 @@ +/* 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.apache.zest.ide.plugin.idea.sideEffects.common; + +/** + * @author edward.yakop@gmail.com + * @since 0.1 + */ +public final class Qi4jSideEffectConstants +{ + public static final String QUALIFIED_NAME_SIDE_EFFECTS = "org.qi4j.api.sideeffect.SideEffects"; + + public static final String QUALIFIED_NAME_SIDE_EFFECT_OF = "org.qi4j.api.sideeffect.SideEffectOf"; + public static final String QUALIFIED_NAME_GENERIC_SIDE_EFFECT = "org.qi4j.api.sideeffect.GenericSideEffect"; + + private Qi4jSideEffectConstants() + { + } +} \ No newline at end of file