http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java new file mode 100644 index 0000000..b64d762 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.appliesTo.inspections; + +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.apache.polygene.ide.plugin.idea.common.inspections.AbstractFix; +import org.apache.polygene.ide.plugin.idea.common.inspections.AbstractInspection; + +import java.util.LinkedList; +import java.util.List; + +import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING; +import static org.apache.polygene.ide.plugin.idea.appliesTo.common.PolygeneAppliesToUtil.*; +import static org.apache.polygene.ide.plugin.idea.common.psi.PsiClassUtil.isImplementsInvocationHandler; +import static org.apache.polygene.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope; +import static org.apache.polygene.ide.plugin.idea.common.resource.PolygeneResourceBundle.message; +import static org.apache.polygene.ide.plugin.idea.concerns.common.PolygeneConcernUtil.isAConcern; +import static org.apache.polygene.ide.plugin.idea.concerns.common.PolygeneConcernUtil.isAGenericConcern; +import static org.apache.polygene.ide.plugin.idea.sideEffects.common.PolygeneSideEffectUtil.isAGenericSideEffect; +import static org.apache.polygene.ide.plugin.idea.sideEffects.common.PolygeneSideEffectUtil.isASideEffect; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class AppliesToAnnotationDeclaredCorrectlyInspection extends AbstractInspection +{ + @NotNull + protected final String resourceBundlePrefixId() + { + return "applies.to.annotation.declared.correctly"; + } + + @NotNull + public final String getShortName() + { + return "AppliesToAnnotationDeclaredCorrectlyInspection"; + } + + @Override + public final ProblemDescriptor[] checkClass( @NotNull PsiClass psiClass, + @NotNull InspectionManager manager, + boolean isOnTheFly ) + { + PsiAnnotation appliesToAnnotation = getAppliesToAnnotation( psiClass ); + if( appliesToAnnotation == null ) + { + // If class does not have @AppliesTo, ignore + return null; + } + + String classQualifiedName = psiClass.getQualifiedName(); + // @AppliesTo can only be declared on class + if( psiClass.isInterface() ) + { + // Suggest remove applies to + String message = message( + "applies.to.annotation.declared.correctly.error.annotation.must.be.declared.on.class" + ); + ProblemDescriptor problemDescriptor = createRemoveAppliesToFilterProblemDescriptor( + manager, message, appliesToAnnotation ); + return new ProblemDescriptor[]{ problemDescriptor }; + } + + // If @AppliesTo annotation is empty, ignore + List<PsiAnnotationMemberValue> appliesToAnnotationValues = getAppliesToAnnotationValue( appliesToAnnotation ); + if( appliesToAnnotationValues.isEmpty() ) + { + return null; + } + + // If AppliesToFilter is not resolved, ignore + Project project = psiClass.getProject(); + GlobalSearchScope searchScope = determineSearchScope( psiClass ); + PsiClass appliesToFilterClass = getAppliesToFilterClass( project, searchScope ); + if( appliesToFilterClass == null ) + { + return null; + } + + boolean classIsAConcern = isAConcern( psiClass ); + boolean classIsASideEffect = isASideEffect( psiClass ); + boolean classIsAGenericConcern = classIsAConcern && isAGenericConcern( psiClass ); + boolean classIsAGenericSideEffect = classIsASideEffect && isAGenericSideEffect( psiClass ); + boolean classIsAMixin = !classIsAConcern && !classIsASideEffect; + boolean classIsAGenericMixin = classIsAMixin && isImplementsInvocationHandler( psiClass ); + + List<ProblemDescriptor> problems = new LinkedList<ProblemDescriptor>(); + for( PsiAnnotationMemberValue appliesToAnnotationValue : appliesToAnnotationValues ) + { + PsiJavaCodeReferenceElement appliesToValueClassReference = + getAppliesToValueClassReference( appliesToAnnotationValue ); + + // If it's not a class reference, ignore + if( appliesToValueClassReference == null ) + { + continue; + } + + // If class reference can't be resolved, ignore + PsiClass appliesToValueClass = (PsiClass) appliesToValueClassReference.resolve(); + if( appliesToValueClass == null ) + { + continue; + } + + String appliesToValueQualifiedName = appliesToValueClass.getQualifiedName(); + boolean appliesToValueIsAnAnnotation = appliesToValueClass.isAnnotationType(); + boolean appliesToValueIsImplementingAppliesToFilter = + appliesToValueClass.isInheritor( appliesToFilterClass, true ); + + String message = null; + if( appliesToValueIsAnAnnotation && classIsAMixin ) + { + // If Class is a mixin and appliesToValueClass is an annotation + message = message( + "applies.to.annotation.declared.correctly.error.value.is.invalid.for.mixin", + appliesToValueQualifiedName + ); + } + else if( appliesToValueIsAnAnnotation || appliesToValueIsImplementingAppliesToFilter ) + { + if( classIsAConcern && !classIsAGenericConcern ) + { + // If psiClass is a concern but not generic concern + message = message( + "applies.to.annotation.declared.correctly.error.value.requires.class.to.extends.GenericConcern", + appliesToValueQualifiedName, classQualifiedName + ); + } + else if( classIsASideEffect && !classIsAGenericSideEffect ) + { + // If psiClass a side effect but not a generic side effect + message = message( + "applies.to.annotation.declared.correctly.error.value.requires.class.to.extends.GenericSideEffect", + appliesToValueQualifiedName, classQualifiedName + ); + } + else if( appliesToValueIsImplementingAppliesToFilter && !classIsAGenericMixin ) + { + message = message( + "applies.to.annotation.declared.correctly.error.value.requires.class.to.implements.InvocationHandler", + appliesToValueQualifiedName, classQualifiedName + ); + } + } + else if( appliesToValueClass.isInterface() ) + { + if( !psiClass.isInheritor( appliesToValueClass, true ) && + !( classIsAGenericConcern || classIsAGenericSideEffect ) ) + { + // If psiClass does not implement that interface and it's not a generic concern or generic side effect + if( classIsAConcern ) + { + message = message( + "applies.to.annotation.declared.correctly.error.value.requires.class.to.implement.interface.or.extends.GenericConcern", + appliesToValueQualifiedName, classQualifiedName ); + } + else if( classIsASideEffect ) + { + message = message( + "applies.to.annotation.declared.correctly.error.value.requires.class.to.implement.interface.or.extends.GenericSideEffect", + appliesToValueQualifiedName, classQualifiedName ); + } + else + { + message = message( + "applies.to.annotation.declared.correctly.error.value.requires.class.to.implement.value.interface.or.implements.InvocationHandler", + appliesToValueQualifiedName, classQualifiedName ); + } + } + } + else + { + if( classIsAMixin ) + { + message = message( + "applies.to.annotation.declared.correctly.error.value.is.invalid.for.mixin", + appliesToValueQualifiedName + ); + } + else + { + message = message( + "applies.to.annotation.declared.correctly.error.annotation.value.is.invalid.for.non.mixin", + appliesToValueQualifiedName + ); + } + } + + if( message != null ) + { + ProblemDescriptor problemDescriptor = manager.createProblemDescriptor( + appliesToAnnotationValue, + message, + new RemoveAnnotationValueFix( appliesToAnnotationValue, appliesToValueClassReference ), + GENERIC_ERROR_OR_WARNING ); + problems.add( problemDescriptor ); + } + } + + return problems.toArray( new ProblemDescriptor[problems.size()] ); + } + + @NotNull + private ProblemDescriptor createRemoveAppliesToFilterProblemDescriptor( @NotNull InspectionManager manager, + @NotNull String problemMessage, + @NotNull PsiAnnotation appliesToAnnotation ) + { + RemoveAppliesToFilterAnnotationFix fix = new RemoveAppliesToFilterAnnotationFix( appliesToAnnotation ); + return manager.createProblemDescriptor( appliesToAnnotation, problemMessage, fix, GENERIC_ERROR_OR_WARNING ); + } + + private static class RemoveAppliesToFilterAnnotationFix extends AbstractFix + { + private final PsiAnnotation appliesToFilterAnnotation; + + private RemoveAppliesToFilterAnnotationFix( @NotNull PsiAnnotation appliesToFilterAnnotation ) + { + super( message( "applies.to.annotation.declared.correctly.fix.remove.annotation" ) ); + this.appliesToFilterAnnotation = appliesToFilterAnnotation; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + appliesToFilterAnnotation.delete(); + } + } + + private static class RemoveAnnotationValueFix extends AbstractFix + { + private final PsiAnnotationMemberValue annotationValueToRemove; + + private RemoveAnnotationValueFix( @NotNull PsiAnnotationMemberValue annotationValueToRemove, + @NotNull PsiJavaCodeReferenceElement appliesToValueClassReference ) + { + super( message( "applies.to.annotation.declared.correctly.fix.remove.class.reference", + appliesToValueClassReference.getQualifiedName() ) ); + this.annotationValueToRemove = annotationValueToRemove; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + annotationValueToRemove.delete(); + } + } + + private static class ClassImplementInterfaceFix extends AbstractFix + { + private final PsiClass psiClass; + private final PsiClass interfaceToImplement; + + private ClassImplementInterfaceFix( @NotNull PsiClass psiClass, + @NotNull PsiClass interfaceToImplement ) + { + super( message( "applies.to.annotation.declared.correctly.fix.remove.class.reference", + interfaceToImplement.getQualifiedName() ) ); + this.psiClass = psiClass; + this.interfaceToImplement = interfaceToImplement; + } + + public final void applyFix( @NotNull Project project, @NotNull ProblemDescriptor descriptor ) + { + PsiReferenceList implementList = psiClass.getImplementsList(); + if( implementList != null ) + { + + implementList.add( interfaceToImplement ); + } + } + } + +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java new file mode 100644 index 0000000..b47a045 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.actions; + +import com.intellij.CommonBundle; +import com.intellij.ide.actions.CreateElementActionBase; +import com.intellij.ide.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.JavaTemplateUtil; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.Properties; + +/** + * @author [email protected] + * @since 0.1 + */ +public abstract class AbstractCreateElementActionBase extends CreateElementActionBase +{ + @NonNls + private static final String NAME_TEMPLATE_PROPERTY = "NAME"; + + protected AbstractCreateElementActionBase( String text, String description ) + { + super( text, description, null ); + } + + @NotNull + protected final PsiElement[] invokeDialog( Project project, PsiDirectory directory ) + { + Module module = ModuleUtil.findModuleForFile( directory.getVirtualFile(), project ); + if( module == null ) + { + return PsiElement.EMPTY_ARRAY; + } + + MyInputValidator validator = doInvokeDialog( project, directory ); + return validator.getCreatedElements(); + } + + protected MyInputValidator doInvokeDialog( Project project, PsiDirectory directory ) + { + MyInputValidator validator = new MyInputValidator( project, directory ); + Messages.showInputDialog( project, getDialogPrompt(), getDialogTitle(), Messages.getQuestionIcon(), "", validator ); + return validator; + } + + /** + * @return Dialog prompt. + */ + protected abstract String getDialogPrompt(); + + /** + * @return Dialog title. + */ + protected abstract String getDialogTitle(); + + protected String getErrorTitle() + { + return CommonBundle.getErrorTitle(); + } + + protected final void checkBeforeCreate( String newName, PsiDirectory directory ) + throws IncorrectOperationException + { + JavaDirectoryService javaDirectoryService = JavaDirectoryService.getInstance(); + javaDirectoryService.checkCreateClass( directory, newName ); + } + + protected static PsiClass createClassFromTemplate( @NotNull PsiDirectory directory, + @NotNull String className, + @NotNull String templateName, + @NonNls String... parameters ) + throws IncorrectOperationException + { + String classFileName = className + "." + StdFileTypes.JAVA.getDefaultExtension(); + PsiFile file = createFromTemplateInternal( directory, className, classFileName, templateName, parameters ); + return ( (PsiJavaFile) file ).getClasses()[ 0 ]; + } + + protected static PsiFile createFromTemplateInternal( @NotNull PsiDirectory directory, + @NotNull String name, + @NotNull String fileName, + @NotNull String templateName, + @NonNls String... parameters ) + throws IncorrectOperationException + { + // Load template + FileTemplateManager fileTemplateManager = FileTemplateManager.getInstance(); + FileTemplate template = fileTemplateManager.getJ2eeTemplate( templateName ); + + // Process template properties + Properties properties = new Properties( fileTemplateManager.getDefaultProperties() ); + JavaTemplateUtil.setPackageNameAttribute( properties, directory ); + properties.setProperty( NAME_TEMPLATE_PROPERTY, name ); + + // Add parameters + for( int i = 0; i < parameters.length; i += 2 ) + { + properties.setProperty( parameters[ i ], parameters[ i + 1 ] ); + } + + // Create text from template with specified properties + String text; + try + { + text = template.getText( properties ); + } + catch( Exception e ) + { + String message = "Unable to load template for " + + fileTemplateManager.internalTemplateToSubject( templateName ); + throw new RuntimeException( message, e ); + } + + // Serialized text to file + PsiManager psiManager = PsiManager.getInstance( directory.getProject() ); + PsiFileFactory fileFactory = PsiFileFactory.getInstance( directory.getProject() ); + PsiFile file = fileFactory.createFileFromText( fileName, text ); + + // Reformat the file according to project/default style + CodeStyleManager codeStyleManager = CodeStyleManager.getInstance( psiManager ); + codeStyleManager.reformat( file ); + + // Add newly created file to directory + return (PsiFile) directory.add( file ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/PolygeneCreateActionGroup.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/PolygeneCreateActionGroup.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/PolygeneCreateActionGroup.java new file mode 100644 index 0000000..d53e0e1 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/actions/PolygeneCreateActionGroup.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.actions; + +import com.intellij.ide.IdeView; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.psi.JavaDirectoryService; +import com.intellij.psi.PsiDirectory; + +import static org.apache.polygene.ide.plugin.idea.common.resource.PolygeneResourceBundle.message; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneCreateActionGroup extends DefaultActionGroup +{ + public PolygeneCreateActionGroup() + { + super( message( "polygene.action.group.title" ), true ); + getTemplatePresentation().setDescription( message( "polygene.action.group.description" ) ); + } + + public void update( AnActionEvent e ) + { + Presentation presentation = e.getPresentation(); + presentation.setVisible( shouldActionGroupVisible( e ) ); + } + + private boolean shouldActionGroupVisible( AnActionEvent e ) + { + Module module = e.getData( LangDataKeys.MODULE ); + if( module == null ) + { + return false; + } + + // TODO: Enable this once PolygeneFacet can be automatically added/removed +// if( PolygeneFacet.getInstance( module ) == null ) +// { +// return false; +// } + + // Are we on IDE View and under project source folder? + Project project = e.getData( PlatformDataKeys.PROJECT ); + IdeView view = e.getData( LangDataKeys.IDE_VIEW ); + if( view != null && project != null ) + { + ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance( project ).getFileIndex(); + PsiDirectory[] dirs = view.getDirectories(); + for( PsiDirectory dir : dirs ) + { + if( projectFileIndex.isInSourceContent( dir.getVirtualFile() ) && JavaDirectoryService.getInstance().getPackage( dir ) != null ) + { + return true; + } + } + } + + return false; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacet.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacet.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacet.java new file mode 100644 index 0000000..7342707 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacet.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.facet; + +import com.intellij.facet.Facet; +import com.intellij.facet.FacetManager; +import com.intellij.facet.FacetType; +import com.intellij.openapi.module.Module; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneFacet extends Facet<PolygeneFacetConfiguration> +{ + public PolygeneFacet( @NotNull FacetType facetType, + @NotNull Module module, + String name, + @NotNull PolygeneFacetConfiguration configuration, + Facet underlyingFacet + ) + { + super( facetType, module, name, configuration, underlyingFacet ); + } + + @Nullable + public static PolygeneFacet getInstance( @NotNull Module module ) + { + return FacetManager.getInstance( module ).getFacetByType( PolygeneFacetType.ID ); + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetConfiguration.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetConfiguration.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetConfiguration.java new file mode 100644 index 0000000..6a6f28e --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetConfiguration.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.facet; + +import com.intellij.facet.FacetConfiguration; +import com.intellij.facet.ui.FacetEditorContext; +import com.intellij.facet.ui.FacetEditorTab; +import com.intellij.facet.ui.FacetValidatorsManager; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import org.jdom.Element; +import org.apache.polygene.ide.plugin.idea.common.facet.ui.PolygeneFacetEditorTab; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneFacetConfiguration + implements FacetConfiguration +{ + public FacetEditorTab[] createEditorTabs( FacetEditorContext editorContext, + FacetValidatorsManager validatorsManager ) + { + return new FacetEditorTab[]{ + new PolygeneFacetEditorTab( editorContext ) + }; + } + + public final void readExternal( Element element ) + throws InvalidDataException + { + // Do nothing + } + + public final void writeExternal( Element element ) + throws WriteExternalException + { + // Do nothing + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetType.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetType.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetType.java new file mode 100644 index 0000000..b45de9f --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/PolygeneFacetType.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.facet; + +import com.intellij.facet.Facet; +import com.intellij.facet.FacetType; +import com.intellij.facet.FacetTypeId; +import com.intellij.facet.autodetecting.FacetDetector; +import com.intellij.facet.autodetecting.FacetDetectorRegistry; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.module.JavaModuleType; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.vfs.VirtualFileFilter; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiImportStatement; +import com.intellij.psi.PsiReferenceExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneFacetType extends FacetType<PolygeneFacet, PolygeneFacetConfiguration> +{ + public static final FacetTypeId<PolygeneFacet> ID = new FacetTypeId<PolygeneFacet>(); + + public PolygeneFacetType() + { + super( ID, "PolygeneFacet", "Polygene Facet" ); + } + + public final PolygeneFacetConfiguration createDefaultConfiguration() + { + return new PolygeneFacetConfiguration(); + } + + public final PolygeneFacet createFacet( @NotNull Module module, + String name, + @NotNull PolygeneFacetConfiguration configuration, + @Nullable Facet underlyingFacet ) + { + return new PolygeneFacet( this, module, name, configuration, underlyingFacet ); + } + + public final boolean isSuitableModuleType( ModuleType moduleType ) + { + return moduleType instanceof JavaModuleType; + } + + @Override + public final void registerDetectors( FacetDetectorRegistry<PolygeneFacetConfiguration> registry ) + { + registry.registerOnTheFlyDetector( + StdFileTypes.JAVA, VirtualFileFilter.ALL, new HasPolygeneImportPackageCondition(), + new FacetDetector<PsiFile, PolygeneFacetConfiguration>( "PolygeneFacetDetector" ) + { + @Override + public PolygeneFacetConfiguration detectFacet( PsiFile source, + Collection<PolygeneFacetConfiguration> existingConfigurations ) + { + if( !existingConfigurations.isEmpty() ) + { + return existingConfigurations.iterator().next(); + } + + return createDefaultConfiguration(); + } + } + ); + } + + private static class HasPolygeneImportPackageCondition + implements Condition<PsiFile> + { + public final boolean value( PsiFile psiFile ) + { + final boolean[] hasPolygeneImportPackage = new boolean[]{ false }; + + psiFile.accept( new JavaElementVisitor() + { + @Override + public final void visitImportStatement( PsiImportStatement statement ) + { + String packageName = statement.getQualifiedName(); + if( packageName != null && packageName.startsWith( "org.apache.polygene" ) ) + { + hasPolygeneImportPackage[ 0 ] = true; + } + } + + @Override + public void visitReferenceExpression( PsiReferenceExpression expression ) + { + // Ignore + } + } ); + return hasPolygeneImportPackage[ 0 ]; + } + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/ui/PolygeneFacetEditorTab.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/ui/PolygeneFacetEditorTab.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/ui/PolygeneFacetEditorTab.java new file mode 100644 index 0000000..dc4b121 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/facet/ui/PolygeneFacetEditorTab.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.facet.ui; + +import com.intellij.facet.ui.FacetEditorContext; +import com.intellij.facet.ui.FacetEditorTab; +import com.intellij.openapi.options.ConfigurationException; +import org.jetbrains.annotations.Nls; + +import javax.swing.*; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneFacetEditorTab extends FacetEditorTab +{ + private final FacetEditorContext editorContext; + + public PolygeneFacetEditorTab( FacetEditorContext aContext ) + { + editorContext = aContext; + } + + @Nls + public final String getDisplayName() + { + return "Polygene"; + } + + public JComponent createComponent() + { + return new JPanel(); + } + + public final boolean isModified() + { + return false; + } + + public final void apply() + throws ConfigurationException + { + // From UI to configuration + } + + public final void reset() + { + // From Configuration to UI + } + + public final void disposeUIResources() + { + // Do nothing for now + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractFix.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractFix.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractFix.java new file mode 100644 index 0000000..1369766 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractFix.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.inspections; + +import com.intellij.codeInspection.LocalQuickFix; +import org.jetbrains.annotations.NotNull; + +import static org.apache.polygene.ide.plugin.idea.common.resource.PolygeneResourceBundle.message; + +/** + * @author [email protected] + * @since 0.1 + */ +public abstract class AbstractFix + implements LocalQuickFix +{ + private String fixName; + + protected AbstractFix( @NotNull String name ) + { + fixName = name; + } + + @NotNull + public final String getName() + { + return fixName; + } + + @NotNull + public final String getFamilyName() + { + return message( "polygene.quick.fixes.family.name" ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractInspection.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractInspection.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractInspection.java new file mode 100644 index 0000000..a6d0a38 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/inspections/AbstractInspection.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.inspections; + +import com.intellij.codeHighlighting.HighlightDisplayLevel; +import com.intellij.codeInspection.BaseJavaLocalInspectionTool; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.apache.polygene.ide.plugin.idea.common.resource.PolygeneResourceBundle; + +import static com.intellij.codeHighlighting.HighlightDisplayLevel.ERROR; + +/** + * @author [email protected] + * @since 0.1 + */ +public abstract class AbstractInspection extends BaseJavaLocalInspectionTool +{ + private static final String POLYGENE_IDEA_INSPECTIONS_NAME = "polygene.inspections.name"; + + @Nls @NotNull public String getGroupDisplayName() + { + return PolygeneResourceBundle.message( POLYGENE_IDEA_INSPECTIONS_NAME ); + } + + @NotNull + protected abstract String resourceBundlePrefixId(); + + @Nls @NotNull + public final String getDisplayName() + { + return PolygeneResourceBundle.message( resourceBundlePrefixId() + ".name.display" ); + } + + @NotNull @Override + public HighlightDisplayLevel getDefaultLevel() + { + return ERROR; + } + + @Override + public boolean isEnabledByDefault() + { + return true; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/intentions/AbstractIntention.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/intentions/AbstractIntention.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/intentions/AbstractIntention.java new file mode 100644 index 0000000..d50693f --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/intentions/AbstractIntention.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.intentions; + +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.openapi.editor.CaretModel; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.ReadonlyStatusHandler; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static org.apache.polygene.ide.plugin.idea.common.resource.PolygeneResourceBundle.message; + +/** + * This class is based from {@code com.siyeh.ipp.base.Intention} + * + * @author [email protected] + */ +public abstract class AbstractIntention extends PsiElementBaseIntentionAction +{ + protected abstract boolean isIntentionValidFor( PsiElement element ); + + protected abstract String resourceBundlePrefixId(); + + /** + * Implement this method to process intention. + * + * @param project The project in which the availability is checked. + * @param editor The editor in which the intention will be invoked. + * @param element The element under caret. + */ + protected abstract void processIntention( @NotNull Project project, + @NotNull Editor editor, + @NotNull PsiElement element ); + + public void invoke( @NotNull Project project, Editor editor, PsiFile file ) + throws IncorrectOperationException + { + if( isFileReadOnly( project, file ) ) + { + return; + } + + final PsiElement element = findMatchingElement( file, editor ); + if( element == null ) + { + return; + } + + processIntention( project, editor, element ); + } + + protected static boolean isFileReadOnly( @NotNull Project project, @NotNull PsiFile file ) + { + VirtualFile virtualFile = file.getVirtualFile(); + ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance( project ); + ReadonlyStatusHandler.OperationStatus operationStatus = + readonlyStatusHandler.ensureFilesWritable( virtualFile ); + return operationStatus.hasReadonlyFiles(); + } + + @Nullable + private PsiElement findMatchingElement( @NotNull PsiFile file, @NotNull Editor editor ) + { + CaretModel caretModel = editor.getCaretModel(); + int position = caretModel.getOffset(); + PsiElement element = file.findElementAt( position ); + return findMatchingElement( element ); + } + + @Nullable + private PsiElement findMatchingElement( @Nullable PsiElement element ) + { + while( element != null ) + { + if( isIntentionValidFor( element ) ) + { + return element; + } + else + { + element = element.getParent(); + if( element instanceof PsiFile ) + { + break; + } + } + } + + return null; + } + + @Override + public boolean isAvailable( @NotNull Project project, Editor editor, @Nullable PsiElement element ) + { + return isIntentionValidFor( element ); + } + + @NotNull + public final String getFamilyName() + { + return message( resourceBundlePrefixId() + ".family.name" ); + } + + @NotNull + @Override + public final String getText() + { + return message( resourceBundlePrefixId() + ".name" ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiAnnotationUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiAnnotationUtil.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiAnnotationUtil.java new file mode 100644 index 0000000..4840a04 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiAnnotationUtil.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.psi; + +import com.intellij.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedList; +import java.util.List; + +import static java.util.Collections.emptyList; + +/** + * @author [email protected] + * @since 0.1 + */ +public class PsiAnnotationUtil +{ + @NotNull + public static List<PsiAnnotationMemberValue> getAnnotationDefaultParameterValue( @Nullable PsiAnnotation annotation ) + { + if( annotation == null ) + { + return emptyList(); + } + + List<PsiAnnotationMemberValue> defaultParameterValues = new LinkedList<PsiAnnotationMemberValue>(); + + PsiAnnotationParameterList list = annotation.getParameterList(); + PsiNameValuePair[] attributes = list.getAttributes(); + for( PsiNameValuePair valuePair : attributes ) + { + String parameterName = valuePair.getName(); + if( parameterName == null || PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals( parameterName ) ) + { + PsiAnnotationMemberValue value = valuePair.getValue(); + if( value == null ) + { + continue; + } + + if( value instanceof PsiArrayInitializerMemberValue ) + { + // If It's an array + PsiArrayInitializerMemberValue valueWrapper = (PsiArrayInitializerMemberValue) value; + PsiAnnotationMemberValue[] values = valueWrapper.getInitializers(); + for( PsiAnnotationMemberValue psiAnnotationMemberValue : values ) + { + if( psiAnnotationMemberValue != null ) + { + defaultParameterValues.add( psiAnnotationMemberValue ); + } + } + } + else + { + // If there's only one value + defaultParameterValues.add( value ); + } + + break; + } + } + + return defaultParameterValues; + } + + @Nullable + public static PsiJavaCodeReferenceElement getClassReference( @NotNull PsiAnnotationMemberValue value ) + { + if( value instanceof PsiClassObjectAccessExpression ) + { + PsiClassObjectAccessExpression objectAccessExpression = (PsiClassObjectAccessExpression) value; + PsiTypeElement typeElement = objectAccessExpression.getOperand(); + return typeElement.getInnermostComponentReferenceElement(); + } + + return null; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiClassUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiClassUtil.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiClassUtil.java new file mode 100644 index 0000000..1f7dbb2 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/PsiClassUtil.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.psi; + +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.InvocationHandler; +import java.util.HashSet; +import java.util.Set; + +import static org.apache.polygene.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PsiClassUtil +{ + @Nullable + public static PsiClass getPSIClass( @NotNull PsiElement element ) + { + if( element instanceof PsiClass ) + { + return (PsiClass) element; + } + + if( element instanceof PsiTypeElement ) + { + PsiTypeElement psiTypeElement = (PsiTypeElement) element; + PsiJavaCodeReferenceElement componentRef = psiTypeElement.getInnermostComponentReferenceElement(); + if( componentRef == null ) + { + return null; + } + + return (PsiClass) componentRef.resolve(); + } + + PsiElement context = element.getContext(); + if( context instanceof PsiClass ) + { + return (PsiClass) context; + } + + return null; + } + + @NotNull + public static Set<PsiClass> getExtends( @NotNull PsiClass psiClass ) + { + HashSet<PsiClass> extendsClasses = new HashSet<PsiClass>(); + PsiClassType[] extendsClassTypes = psiClass.getExtendsListTypes(); + for( PsiClassType extendClassType : extendsClassTypes ) + { + PsiClass extendClass = extendClassType.resolve(); + if( extendClass != null ) + { + extendsClasses.add( extendClass ); + } + } + + return extendsClasses; + } + + /** + * Returns all extends of the specified {@code psiClass}. + * + * @param psiClass class to process. + * @return all extends of the specified {@code psiClass}. + * @since 0.1 + */ + @NotNull + public static Set<PsiClass> getExtendsDeep( @NotNull PsiClass psiClass ) + { + HashSet<PsiClass> extendsClasses = new HashSet<PsiClass>(); + PsiClassType[] extendsClassTypes = psiClass.getExtendsListTypes(); + for( PsiClassType extendClassType : extendsClassTypes ) + { + PsiClass extendClass = extendClassType.resolve(); + if( extendClass != null ) + { + extendsClasses.add( extendClass ); + extendsClasses.addAll( getExtendsDeep( extendClass ) ); + } + } + + return extendsClasses; + } + + /** + * @param psiClass Psi class to check. + * @return {@code true} if psi class implements {@code InvocationHandler}, {@code false} otherwise. + * @see InvocationHandler + */ + public static boolean isImplementsInvocationHandler( @NotNull PsiClass psiClass ) + { + if( psiClass.isInterface() ) + { + return false; + } + + GlobalSearchScope searchScope = determineSearchScope( psiClass ); + assert searchScope != null; + + JavaPsiFacade psiFacade = JavaPsiFacade.getInstance( psiClass.getProject() ); + PsiClass invocationHandler = psiFacade.findClass( "java.lang.reflect.InvocationHandler", searchScope ); + assert invocationHandler != null; + + return psiClass.isInheritor( invocationHandler, true ); + } + + private PsiClassUtil() + { + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java new file mode 100644 index 0000000..4beda1f --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.psi.search; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.openapi.module.ModuleUtil.findModuleForPsiElement; +import static org.apache.polygene.ide.plugin.idea.common.vfs.VirtualFileUtil.getVirtualFile; + +/** + * @author [email protected] + * @since 0.1 + */ +public class GlobalSearchScopeUtil +{ + /** + * Determine search scope given a psi element. + * + * @param psiElement context. + * @return Search scope given psi class. + * @since 0.1 + */ + @Nullable + public static GlobalSearchScope determineSearchScope( @NotNull PsiElement psiElement ) + { + VirtualFile classVirtualFile = getVirtualFile( psiElement ); + if( classVirtualFile == null ) + { + return null; + } + + Module module = findModuleForPsiElement( psiElement ); + if( module == null ) + { + return null; + } + + Project project = psiElement.getProject(); + ProjectRootManager projectRootManager = ProjectRootManager.getInstance( project ); + boolean includeTestClasses = projectRootManager.getFileIndex().isInTestSourceContent( classVirtualFile ); + return module.getModuleWithDependenciesAndLibrariesScope( includeTestClasses ); + } + +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/resource/PolygeneResourceBundle.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/resource/PolygeneResourceBundle.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/resource/PolygeneResourceBundle.java new file mode 100644 index 0000000..c5f29b4 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/resource/PolygeneResourceBundle.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.resource; + +import com.intellij.CommonBundle; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.PropertyKey; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.ResourceBundle; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneResourceBundle +{ + + @NonNls + private static final String RESOURCE_BUNDLE_NAME = "org.apache.polygene.ide.plugin.idea.common.resource.PolygeneResourceBundle"; + + private static Reference<ResourceBundle> BUNDLE_REF; + + private PolygeneResourceBundle() + { + } + + public static String message( @PropertyKey( resourceBundle = RESOURCE_BUNDLE_NAME ) String key, + Object... params ) + { + ResourceBundle resourceBundle = getBundle(); + return CommonBundle.message( resourceBundle, key, params ); + } + + private static ResourceBundle getBundle() + { + ResourceBundle bundle = null; + if( BUNDLE_REF != null ) + { + bundle = BUNDLE_REF.get(); + } + + if( bundle == null ) + { + bundle = ResourceBundle.getBundle( PolygeneResourceBundle.class.getName() ); + BUNDLE_REF = new SoftReference<ResourceBundle>( bundle ); + } + + return bundle; + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/vfs/VirtualFileUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/vfs/VirtualFileUtil.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/vfs/VirtualFileUtil.java new file mode 100644 index 0000000..d5b95b6 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/common/vfs/VirtualFileUtil.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.common.vfs; + +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileSystemItem; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class VirtualFileUtil +{ + /** + * @param element element to process. + * @return The containing virtual file of the element. + * @since 0.1 + */ + @Nullable + public static VirtualFile getVirtualFile( @NotNull PsiElement element ) + { + if( element instanceof PsiFileSystemItem ) + { + PsiFileSystemItem fileSystemItem = (PsiFileSystemItem) element; + return fileSystemItem.getVirtualFile(); + } + + // If it's not a file system, assume that this is an element within a file + PsiFile containingFile = element.getContainingFile(); + if( containingFile == null ) + { + return null; + } + + VirtualFile virtualFile = containingFile.getVirtualFile(); + if( virtualFile != null ) + { + return virtualFile; + } + + PsiFile originalFile = containingFile.getOriginalFile(); + if( originalFile == null ) + { + return null; + } + + return originalFile.getVirtualFile(); + } + + private VirtualFileUtil() + { + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/CreateConcernFromMixinTypeOrCompositeAction.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/CreateConcernFromMixinTypeOrCompositeAction.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/CreateConcernFromMixinTypeOrCompositeAction.java new file mode 100644 index 0000000..b6abdfb --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/CreateConcernFromMixinTypeOrCompositeAction.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.concerns.actions.create; + +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.apache.polygene.ide.plugin.idea.common.actions.AbstractCreateElementActionBase; + +/** + * @author [email protected] + */ +public class CreateConcernFromMixinTypeOrCompositeAction extends AbstractCreateElementActionBase +{ + public CreateConcernFromMixinTypeOrCompositeAction() + { + super( "TODO", "TODO" ); + } + + protected String getCommandName() + { + return "CreateConcernFromMixinTypeOrCompositeAction"; + } + + protected String getActionName( PsiDirectory directory, String newName ) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected String getDialogPrompt() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + protected String getDialogTitle() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @NotNull + protected PsiElement[] create( String newName, PsiDirectory directory ) + throws Exception + { + return new PsiElement[0]; //To change body of implemented methods use File | Settings | File Templates. + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.java new file mode 100644 index 0000000..402df3e --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.concerns.actions.create.inPackage; + +import com.intellij.ide.actions.CreateInPackageActionBase; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.JavaDirectoryService; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.openapi.actionSystem.DataKeys.PROJECT; +import static com.intellij.openapi.actionSystem.DataKeys.PSI_ELEMENT; +import static com.intellij.util.Icons.CLASS_ICON; +import static org.apache.polygene.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope; +import static org.apache.polygene.ide.plugin.idea.common.resource.PolygeneResourceBundle.message; +import static org.apache.polygene.ide.plugin.idea.concerns.common.PolygeneConcernConstants.TEMPLATE_GENERIC_CONCERN_OF; +import static org.apache.polygene.ide.plugin.idea.concerns.common.PolygeneConcernUtil.getConcernOfClass; + +/** + * JAVADOC: Non generic concern + * + * @author [email protected] + * @since 0.1 + */ +public class CreateConcernOfInPackageAction extends CreateInPackageActionBase +{ + protected CreateConcernOfInPackageAction() + { + super( message( "createConcernOfInPackage.menu.action.text" ), + message( "createConcernOfInPackage.menu.action.description" ), + CLASS_ICON ); + } + + @Override + protected final boolean isAvailable( DataContext dataContext ) + { + boolean isAvailable = super.isAvailable( dataContext ); + if( !isAvailable ) + { + return false; + } + + PsiElement psiElement = PSI_ELEMENT.getData( dataContext ); + if( psiElement == null ) + { + return false; + } + + GlobalSearchScope searchScope = determineSearchScope( psiElement ); + if( searchScope == null ) + { + return false; + } + + Project project = PROJECT.getData( dataContext ); + PsiClass psiClass = getConcernOfClass( project, searchScope ); + return psiClass != null; + } + + @NotNull + protected final PsiElement[] invokeDialog( Project project, PsiDirectory directory ) + { + MyInputValidator validator = new MyInputValidator( project, directory ); + Messages.showInputDialog( project, message( "createConcernOfInPackage.dlg.prompt" ), + message( "createConcernOfInPackage.dlg.title" ), + Messages.getQuestionIcon(), "", validator ); + return validator.getCreatedElements(); + } + + protected final String getCommandName() + { + return message( "createConcernOfInPackage.command.name" ); + } + + protected final String getErrorTitle() + { + return message( "createConcernOfInPackage.error.title" ); + } + + protected final String getActionName( PsiDirectory directory, String newName ) + { + return message( "createConcernOfInPackage.progress.text", newName ); + } + + protected final void doCheckCreate( final PsiDirectory dir, final String className ) + throws IncorrectOperationException + { + JavaDirectoryService javaDirectoryService = JavaDirectoryService.getInstance(); + javaDirectoryService.checkCreateClass( dir, className ); + } + + @NotNull + protected PsiClass doCreate( final PsiDirectory dir, final String className ) + throws IncorrectOperationException + { + JavaDirectoryService javaDirectoryService = JavaDirectoryService.getInstance(); + return javaDirectoryService.createClass( dir, className, TEMPLATE_GENERIC_CONCERN_OF ); + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernConstants.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernConstants.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernConstants.java new file mode 100644 index 0000000..0bbe3a1 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernConstants.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.concerns.common; + +import org.jetbrains.annotations.NonNls; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneConcernConstants +{ + public static final String QUALIFIED_NAME_CONCERNS = "org.apache.polygene.api.concern.Concerns"; + + public static final String QUALIFIED_NAME_CONCERN_OF = "org.apache.polygene.api.concern.ConcernOf"; + public static final String QUALIFIED_NAME_GENERIC_CONCERN = "org.apache.polygene.api.concern.GenericConcern"; + + @NonNls + public static final String TEMPLATE_GENERIC_CONCERN_OF = "GenericConcernOf.java"; + + private PolygeneConcernConstants() + { + } +} http://git-wip-us.apache.org/repos/asf/zest-java/blob/e0e1d7d4/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernUtil.java ---------------------------------------------------------------------- diff --git a/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernUtil.java b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernUtil.java new file mode 100644 index 0000000..4499143 --- /dev/null +++ b/tools/qidea/src/main/java/org/apache/polygene/ide/plugin/idea/concerns/common/PolygeneConcernUtil.java @@ -0,0 +1,228 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.polygene.ide.plugin.idea.concerns.common; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import static com.intellij.codeInsight.AnnotationUtil.findAnnotation; +import static java.util.Collections.emptyList; +import static org.apache.polygene.ide.plugin.idea.common.psi.PsiAnnotationUtil.getAnnotationDefaultParameterValue; +import static org.apache.polygene.ide.plugin.idea.common.psi.PsiAnnotationUtil.getClassReference; +import static org.apache.polygene.ide.plugin.idea.common.psi.PsiClassUtil.getPSIClass; +import static org.apache.polygene.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope; +import static org.apache.polygene.ide.plugin.idea.concerns.common.PolygeneConcernConstants.*; + +/** + * @author [email protected] + * @since 0.1 + */ +public final class PolygeneConcernUtil +{ + + + /** + * @param searchContext Search context. + * @return {@code GenericConcern} psi class if found, {@code null} otherwise. + * @since 0.1 + */ + @Nullable + public static PsiClass getGenericConcernClass( @NotNull PsiElement searchContext ) + { + Project project = searchContext.getProject(); + GlobalSearchScope searchScope = determineSearchScope( searchContext ); + return getGenericConcernClass( project, searchScope ); + } + + /** + * @param project project. + * @param scope search scope. + * @return {@code GenericConcern} psi class if found, {@code null} otherwise. + * @since 0.1 + */ + @Nullable + public static PsiClass getGenericConcernClass( @NotNull Project project, + @Nullable GlobalSearchScope scope ) + { + JavaPsiFacade psiFacade = JavaPsiFacade.getInstance( project ); + return scope != null ? psiFacade.findClass( QUALIFIED_NAME_GENERIC_CONCERN, scope ) : null; + } + + @Nullable + public static PsiClass getConcernOfClass( @NotNull PsiElement searchContext ) + { + Project project = searchContext.getProject(); + GlobalSearchScope searchScope = determineSearchScope( searchContext ); + return getConcernOfClass( project, searchScope ); + } + + @Nullable + public static PsiClass getConcernOfClass( @NotNull Project project, + @Nullable GlobalSearchScope scope ) + { + JavaPsiFacade psiFacade = JavaPsiFacade.getInstance( project ); + return scope != null ? psiFacade.findClass( QUALIFIED_NAME_CONCERN_OF, scope ) : null; + } + + @Nullable + public static PsiAnnotation getConcernsAnnotation( @NotNull PsiElement element ) + { + PsiClass psiClass = getPSIClass( element ); + return findAnnotation( psiClass, QUALIFIED_NAME_CONCERNS ); + } + + @NotNull + public static PsiAnnotation addOrReplaceConcernAnnotation( @NotNull PsiModifierListOwner modifierListOwner, + @NotNull PsiClass concernClassToAdd ) + { + Project project = modifierListOwner.getProject(); + JavaPsiFacade psiFacade = JavaPsiFacade.getInstance( project ); + PsiElementFactory factory = psiFacade.getElementFactory(); + PsiAnnotation existingConcernsAnnotation = findAnnotation( modifierListOwner, QUALIFIED_NAME_CONCERNS ); + + boolean isReplace = false; + PsiAnnotation newConcernsAnnotation; + if( existingConcernsAnnotation != null ) + { + // Check duplicate + List<PsiAnnotationMemberValue> concernsValues = getConcernsAnnotationValue( existingConcernsAnnotation ); + for( PsiAnnotationMemberValue concernValue : concernsValues ) + { + PsiJavaCodeReferenceElement concernClassReference = getConcernClassReference( concernValue ); + if( concernClassReference == null ) + { + continue; + } + + PsiElement concernClass = concernClassReference.resolve(); + if( concernClassToAdd.equals( concernClass ) ) + { + return existingConcernsAnnotation; + } + } + + isReplace = true; + } + + String concernAnnotationText = createConcernAnnotationText( existingConcernsAnnotation, concernClassToAdd ); + newConcernsAnnotation = + factory.createAnnotationFromText( concernAnnotationText, modifierListOwner ); + + if( isReplace ) + { + // Replace @Concerns instead + existingConcernsAnnotation.replace( newConcernsAnnotation ); + } + else + { + // @Concerns doesn't exists, add it as first child + PsiModifierList modifierList = modifierListOwner.getModifierList(); + modifierList.addBefore( newConcernsAnnotation, modifierList.getFirstChild() ); + } + + // Shorten all class references if possible + JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance( project ); + codeStyleManager.shortenClassReferences( newConcernsAnnotation ); + + return newConcernsAnnotation; + } + + @NotNull + private static String createConcernAnnotationText( @Nullable PsiAnnotation concernAnnotationBase, + @NotNull PsiClass concernClassToAdd ) + { + StringBuilder annotationTextBuilder = new StringBuilder(); + annotationTextBuilder.append( "@" ).append( QUALIFIED_NAME_CONCERNS ).append( "( {" ); + List<PsiAnnotationMemberValue> concernsAnnotationValue = getConcernsAnnotationValue( concernAnnotationBase ); + for( PsiAnnotationMemberValue concernValue : concernsAnnotationValue ) + { + annotationTextBuilder.append( concernValue.getText() ).append( ", " ); + } + annotationTextBuilder.append( concernClassToAdd.getQualifiedName() ).append( ".class" ); + annotationTextBuilder.append( "} )" ); + return annotationTextBuilder.toString(); + } + + @NotNull + public static List<PsiAnnotationMemberValue> getConcernsAnnotationValue( @Nullable PsiAnnotation concernsAnnotation ) + { + if( concernsAnnotation == null ) + { + return emptyList(); + } + + String concernsQualifiedName = concernsAnnotation.getQualifiedName(); + if( !QUALIFIED_NAME_CONCERNS.equals( concernsQualifiedName ) ) + { + return emptyList(); + } + + return getAnnotationDefaultParameterValue( concernsAnnotation ); + } + + @Nullable + public static PsiJavaCodeReferenceElement getConcernClassReference( @NotNull PsiAnnotationMemberValue value ) + { + return getClassReference( value ); + } + + /** + * @param psiClass psi class to check. + * @return {@code true} if {@code psiClass} extends {@code ConcernOf}, {@code false} if {@code psiClass} does + * not extends {@code ConcernOf} or {@code ConcernOf} is not found. + * @since 0.1 + */ + public static boolean isAConcern( @NotNull PsiClass psiClass ) + { + if( psiClass.isInterface() ) + { + return false; + } + + PsiClass concernOfClass = getConcernOfClass( psiClass ); + return concernOfClass != null && psiClass.isInheritor( concernOfClass, true ); + } + + /** + * @param psiClass psi class to check. + * @return {@code true} if {@code psiClass} extends {@code GenericConcern}, {@code false} if {@code psiClass} does + * not extends {@code GenericConcern} or {@code GenericConcern} is not found. + * @since 0.1 + */ + public static boolean isAGenericConcern( @NotNull PsiClass psiClass ) + { + if( psiClass.isInterface() ) + { + return false; + } + + PsiClass genericConcern = getGenericConcernClass( psiClass ); + return genericConcern != null && psiClass.isInheritor( genericConcern, true ); + } + + private PolygeneConcernUtil() + { + } +}
