You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by ni...@apache.org on 2015/07/30 21:48:55 UTC
[56/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/appliesTo/common/Qi4jAppliesToUtil.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/appliesTo/common/Qi4jAppliesToUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/appliesTo/common/Qi4jAppliesToUtil.java
new file mode 100644
index 0000000..242e4e6
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/appliesTo/common/Qi4jAppliesToUtil.java
@@ -0,0 +1,136 @@
+/* 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.appliesTo.common;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+
+import static com.intellij.codeInsight.AnnotationUtil.findAnnotation;
+import static java.util.Collections.emptyList;
+import static org.apache.zest.ide.plugin.idea.appliesTo.common.Qi4jAppliesToConstants.QUALIFIED_NAME_APPLIES_TO;
+import static org.apache.zest.ide.plugin.idea.appliesTo.common.Qi4jAppliesToConstants.QUALIFIED_NAME_APPLIES_TO_FILTER;
+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.getPSIClass;
+import static org.apache.zest.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jAppliesToUtil
+{
+ /**
+ * @param searchContext Search context.
+ * @return {@code AppliesToFilter} class given the search context. {@code null} if not found.
+ * @since 0.1
+ */
+ @Nullable
+ public static PsiClass getAppliesToFilterClass( @NotNull PsiElement searchContext )
+ {
+ Project project = searchContext.getProject();
+ GlobalSearchScope searchScope = determineSearchScope( searchContext );
+ return getAppliesToFilterClass( project, searchScope );
+ }
+
+ /**
+ * @param project project.
+ * @param scope search scope.
+ * @return {@code AppliesToFilter} class given {@code project} and {@code scope} parameters.
+ * Returns {@code null} if not found.
+ * @since 0.1
+ */
+ @Nullable
+ public static PsiClass getAppliesToFilterClass( @NotNull Project project,
+ @Nullable GlobalSearchScope scope )
+ {
+ JavaPsiFacade psiFacade = JavaPsiFacade.getInstance( project );
+ return scope == null ? null : psiFacade.findClass( QUALIFIED_NAME_APPLIES_TO_FILTER, scope );
+ }
+
+ /**
+ * @param elementWithinJavaClass element within java class.
+ * @return {@code @AppliesTo} annotation declaration of the class that contains the element.
+ * Returns {@code null} if not found, or {@code element} is an invalid context.
+ * @since 0.1
+ */
+ @Nullable
+ public static PsiAnnotation getAppliesToAnnotation( @NotNull PsiElement elementWithinJavaClass )
+ {
+ PsiClass psiClass = getPSIClass( elementWithinJavaClass );
+ return findAnnotation( psiClass, QUALIFIED_NAME_APPLIES_TO );
+ }
+
+ /**
+ * @param annotation annotation to process.
+ * @return {@code @AppliesTo} annotation value. Returns {@link Collections#emptyList()} if {@code annotation} is
+ * {@code null} or annotation is not a {@code @AppliesTo} annotation.
+ * @since 0.1
+ */
+ @NotNull
+ public static List<PsiAnnotationMemberValue> getAppliesToAnnotationValue( @Nullable PsiAnnotation annotation )
+ {
+ if( annotation == null )
+ {
+ return emptyList();
+ }
+
+ String concernsQualifiedName = annotation.getQualifiedName();
+ if( !QUALIFIED_NAME_APPLIES_TO.equals( concernsQualifiedName ) )
+ {
+ return emptyList();
+ }
+
+ return getAnnotationDefaultParameterValue( annotation );
+ }
+
+ /**
+ * @param value annotation member value.
+ * @return Applies to class reference given the {@code value} parameter. Returns {@code null} if it's not a
+ * class reference.
+ * @since 0.1
+ */
+ @Nullable
+ public static PsiJavaCodeReferenceElement getAppliesToValueClassReference( @NotNull PsiAnnotationMemberValue value )
+ {
+ return getClassReference( value );
+ }
+
+ /**
+ * Returns a {@code boolean} indicator whether the specified {@code psiClass} is implements
+ * {@code AppliesToFilter} class.
+ *
+ * @param psiClass class to check.
+ * @param appliesToFilterClass {@code AppliesToFilter} class.
+ * @return {@code true} if {@code psiClass} implements {@code AppliesToFilter} class, {@code false} otherwise.
+ * @since 0.1
+ */
+ public static boolean isAnAppliesToFilter( @NotNull PsiClass psiClass, @NotNull PsiClass appliesToFilterClass )
+ {
+ return !psiClass.isInterface() && psiClass.isInheritor( appliesToFilterClass, true );
+ }
+
+ private Qi4jAppliesToUtil()
+ {
+ }
+}
\ 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/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java
new file mode 100644
index 0000000..08641e8
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/appliesTo/inspections/AppliesToAnnotationDeclaredCorrectlyInspection.java
@@ -0,0 +1,292 @@
+/* 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.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.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 static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
+import static org.apache.zest.ide.plugin.idea.appliesTo.common.Qi4jAppliesToUtil.*;
+import static org.apache.zest.ide.plugin.idea.common.psi.PsiClassUtil.isImplementsInvocationHandler;
+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.isAConcern;
+import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernUtil.isAGenericConcern;
+import static org.apache.zest.ide.plugin.idea.sideEffects.common.Qi4jSideEffectUtil.isAGenericSideEffect;
+import static org.apache.zest.ide.plugin.idea.sideEffects.common.Qi4jSideEffectUtil.isASideEffect;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java
new file mode 100644
index 0000000..fb10d31
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/AbstractCreateElementActionBase.java
@@ -0,0 +1,151 @@
+/* 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.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 edward.yakop@gmail.com
+ * @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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/Qi4jCreateActionGroup.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/Qi4jCreateActionGroup.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/Qi4jCreateActionGroup.java
new file mode 100644
index 0000000..1899b51
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/actions/Qi4jCreateActionGroup.java
@@ -0,0 +1,80 @@
+/* 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.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.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jCreateActionGroup extends DefaultActionGroup
+{
+ public Qi4jCreateActionGroup()
+ {
+ super( message( "qi4j.action.group.title" ), true );
+ getTemplatePresentation().setDescription( message( "qi4j.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 Qi4jFacet can be automatically added/removed
+// if( Qi4jFacet.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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacet.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacet.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacet.java
new file mode 100644
index 0000000..eb6d706
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacet.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.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 edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jFacet extends Facet<Qi4jFacetConfiguration>
+{
+ public Qi4jFacet( @NotNull FacetType facetType,
+ @NotNull Module module,
+ String name,
+ @NotNull Qi4jFacetConfiguration configuration,
+ Facet underlyingFacet )
+ {
+ super( facetType, module, name, configuration, underlyingFacet );
+ }
+
+ @Nullable
+ public static Qi4jFacet getInstance( @NotNull Module module )
+ {
+ return FacetManager.getInstance( module ).getFacetByType( Qi4jFacetType.ID );
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetConfiguration.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetConfiguration.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetConfiguration.java
new file mode 100644
index 0000000..fd03600
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetConfiguration.java
@@ -0,0 +1,54 @@
+/* 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.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.zest.ide.plugin.idea.common.facet.ui.Qi4jFacetEditorTab;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jFacetConfiguration
+ implements FacetConfiguration
+{
+ public FacetEditorTab[] createEditorTabs( FacetEditorContext editorContext,
+ FacetValidatorsManager validatorsManager )
+ {
+ return new FacetEditorTab[]{
+ new Qi4jFacetEditorTab( 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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetType.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetType.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetType.java
new file mode 100644
index 0000000..06af246
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/Qi4jFacetType.java
@@ -0,0 +1,120 @@
+/* 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.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 edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jFacetType extends FacetType<Qi4jFacet, Qi4jFacetConfiguration>
+{
+ public static final FacetTypeId<Qi4jFacet> ID = new FacetTypeId<Qi4jFacet>();
+
+ public Qi4jFacetType()
+ {
+ super( ID, "Qi4jFacet", "Zest Facet" );
+ }
+
+ public final Qi4jFacetConfiguration createDefaultConfiguration()
+ {
+ return new Qi4jFacetConfiguration();
+ }
+
+ public final Qi4jFacet createFacet( @NotNull Module module,
+ String name,
+ @NotNull Qi4jFacetConfiguration configuration,
+ @Nullable Facet underlyingFacet )
+ {
+ return new Qi4jFacet( this, module, name, configuration, underlyingFacet );
+ }
+
+ public final boolean isSuitableModuleType( ModuleType moduleType )
+ {
+ return moduleType instanceof JavaModuleType;
+ }
+
+ @Override
+ public final void registerDetectors( FacetDetectorRegistry<Qi4jFacetConfiguration> registry )
+ {
+ registry.registerOnTheFlyDetector(
+ StdFileTypes.JAVA, VirtualFileFilter.ALL, new HasQi4jImportPackageCondition(),
+ new FacetDetector<PsiFile, Qi4jFacetConfiguration>( "Qi4jFacetDetector" )
+ {
+ @Override
+ public Qi4jFacetConfiguration detectFacet( PsiFile source,
+ Collection<Qi4jFacetConfiguration> existingConfigurations )
+ {
+ if( !existingConfigurations.isEmpty() )
+ {
+ return existingConfigurations.iterator().next();
+ }
+
+ return createDefaultConfiguration();
+ }
+ }
+ );
+ }
+
+ private static class HasQi4jImportPackageCondition
+ implements Condition<PsiFile>
+ {
+ public final boolean value( PsiFile psiFile )
+ {
+ final boolean[] hasQi4jImportPackage = new boolean[]{ false };
+
+ psiFile.accept( new JavaElementVisitor()
+ {
+ @Override
+ public final void visitImportStatement( PsiImportStatement statement )
+ {
+ String packageName = statement.getQualifiedName();
+ if( packageName != null && packageName.startsWith( "org.qi4j" ) )
+ {
+ hasQi4jImportPackage[ 0 ] = true;
+ }
+ }
+
+ @Override
+ public void visitReferenceExpression( PsiReferenceExpression expression )
+ {
+ // Ignore
+ }
+ } );
+ return hasQi4jImportPackage[ 0 ];
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/ui/Qi4jFacetEditorTab.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/ui/Qi4jFacetEditorTab.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/ui/Qi4jFacetEditorTab.java
new file mode 100644
index 0000000..7e73b49
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/facet/ui/Qi4jFacetEditorTab.java
@@ -0,0 +1,70 @@
+/* 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.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 edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jFacetEditorTab extends FacetEditorTab
+{
+ private final FacetEditorContext editorContext;
+
+ public Qi4jFacetEditorTab( FacetEditorContext aContext )
+ {
+ editorContext = aContext;
+ }
+
+ @Nls
+ public final String getDisplayName()
+ {
+ return "Zest";
+ }
+
+ 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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractFix.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractFix.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractFix.java
new file mode 100644
index 0000000..c6b9893
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractFix.java
@@ -0,0 +1,49 @@
+/* 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.common.inspections;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import org.jetbrains.annotations.NotNull;
+
+import static org.apache.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @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( "qi4j.quick.fixes.family.name" );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractInspection.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractInspection.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractInspection.java
new file mode 100644
index 0000000..55d59dd
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/inspections/AbstractInspection.java
@@ -0,0 +1,60 @@
+/* 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.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.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle;
+
+import static com.intellij.codeHighlighting.HighlightDisplayLevel.ERROR;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @since 0.1
+ */
+public abstract class AbstractInspection extends BaseJavaLocalInspectionTool
+{
+ private static final String QI4J_IDEA_INSPECTIONS_NAME = "qi4j.inspections.name";
+
+ @Nls @NotNull public String getGroupDisplayName()
+ {
+ return Qi4jResourceBundle.message( QI4J_IDEA_INSPECTIONS_NAME );
+ }
+
+ @NotNull
+ protected abstract String resourceBundlePrefixId();
+
+ @Nls @NotNull
+ public final String getDisplayName()
+ {
+ return Qi4jResourceBundle.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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/intentions/AbstractIntention.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/intentions/AbstractIntention.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/intentions/AbstractIntention.java
new file mode 100644
index 0000000..7bd1b9d
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/intentions/AbstractIntention.java
@@ -0,0 +1,130 @@
+/* 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.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.zest.ide.plugin.idea.common.resource.Qi4jResourceBundle.message;
+
+/**
+ * This class is based from {@code com.siyeh.ipp.base.Intention}
+ *
+ * @author edward.yakop@gmail.com
+ */
+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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiAnnotationUtil.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiAnnotationUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiAnnotationUtil.java
new file mode 100644
index 0000000..f61086d
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiAnnotationUtil.java
@@ -0,0 +1,95 @@
+/* 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.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 edward.yakop@gmail.com
+ * @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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiClassUtil.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiClassUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiClassUtil.java
new file mode 100644
index 0000000..3c330c0
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/PsiClassUtil.java
@@ -0,0 +1,132 @@
+/* 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.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.zest.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java
new file mode 100644
index 0000000..bef9dc4
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/psi/search/GlobalSearchScopeUtil.java
@@ -0,0 +1,65 @@
+/* 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.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.zest.ide.plugin.idea.common.vfs.VirtualFileUtil.getVirtualFile;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/resource/Qi4jResourceBundle.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/resource/Qi4jResourceBundle.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/resource/Qi4jResourceBundle.java
new file mode 100644
index 0000000..72af195
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/resource/Qi4jResourceBundle.java
@@ -0,0 +1,66 @@
+/* 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.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 edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jResourceBundle
+{
+
+ @NonNls
+ private static final String RESOURCE_BUNDLE_NAME = "org.qi4j.ide.plugin.idea.common.resource.Qi4jResourceBundle";
+
+ private static Reference<ResourceBundle> BUNDLE_REF;
+
+ private Qi4jResourceBundle()
+ {
+ }
+
+ 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( Qi4jResourceBundle.class.getName() );
+ BUNDLE_REF = new SoftReference<ResourceBundle>( bundle );
+ }
+
+ return bundle;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/vfs/VirtualFileUtil.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/vfs/VirtualFileUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/vfs/VirtualFileUtil.java
new file mode 100644
index 0000000..29407c1
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/common/vfs/VirtualFileUtil.java
@@ -0,0 +1,71 @@
+/* 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.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 edward.yakop@gmail.com
+ * @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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/actions/create/CreateConcernFromMixinTypeOrCompositeAction.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/actions/create/CreateConcernFromMixinTypeOrCompositeAction.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/actions/create/CreateConcernFromMixinTypeOrCompositeAction.java
new file mode 100644
index 0000000..df4aea1
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/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.zest.ide.plugin.idea.concerns.actions.create;
+
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.apache.zest.ide.plugin.idea.common.actions.AbstractCreateElementActionBase;
+
+/**
+ * @author edward.yakop@gmail.com
+ */
+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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.java
new file mode 100644
index 0000000..a171b20
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/actions/create/inPackage/CreateConcernOfInPackageAction.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.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.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.Qi4jConcernConstants.TEMPLATE_GENERIC_CONCERN_OF;
+import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernUtil.getConcernOfClass;
+
+/**
+ * JAVADOC: Non generic concern
+ *
+ * @author edward.yakop@gmail.com
+ * @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/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernConstants.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernConstants.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernConstants.java
new file mode 100644
index 0000000..9e1dfdd
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernConstants.java
@@ -0,0 +1,38 @@
+/* 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.common;
+
+import org.jetbrains.annotations.NonNls;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jConcernConstants
+{
+ public static final String QUALIFIED_NAME_CONCERNS = "org.qi4j.api.concern.Concerns";
+
+ public static final String QUALIFIED_NAME_CONCERN_OF = "org.qi4j.api.concern.ConcernOf";
+ public static final String QUALIFIED_NAME_GENERIC_CONCERN = "org.qi4j.api.concern.GenericConcern";
+
+ @NonNls
+ public static final String TEMPLATE_GENERIC_CONCERN_OF = "GenericConcernOf.java";
+
+ private Qi4jConcernConstants()
+ {
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-java/blob/fc41bb18/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernUtil.java
----------------------------------------------------------------------
diff --git a/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernUtil.java b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernUtil.java
new file mode 100644
index 0000000..45fb79d
--- /dev/null
+++ b/tools/qidea/src/main/java/org/apache/zest/ide/plugin/idea/concerns/common/Qi4jConcernUtil.java
@@ -0,0 +1,226 @@
+/* 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.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.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.getPSIClass;
+import static org.apache.zest.ide.plugin.idea.common.psi.search.GlobalSearchScopeUtil.determineSearchScope;
+import static org.apache.zest.ide.plugin.idea.concerns.common.Qi4jConcernConstants.*;
+
+/**
+ * @author edward.yakop@gmail.com
+ * @since 0.1
+ */
+public final class Qi4jConcernUtil
+{
+
+
+ /**
+ * @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 Qi4jConcernUtil()
+ {
+ }
+}