polygene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nic...@apache.org
Subject [56/81] [abbrv] zest-java git commit: Stage 2 of the namespace change. Bulk of documentation fixed.
Date Fri, 31 Jul 2015 02:59:36 GMT
http://git-wip-us.apache.org/repos/asf/zest-java/blob/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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/8f20b315/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()
+    {
+    }
+}


Mime
View raw message